【问题标题】:Interface vs Multiple Inheritance In C#C#中的接口与多重继承
【发布时间】:2012-05-13 22:16:27
【问题描述】:

我有一组 A 类和 B 类都有一些属性。和另一个具有自己属性的 C 类。

每当我创建类 C 的实例时,我都想使用 objClassC 访问所有三个类的所有属性。

我如何在 C# 中实现这一点?

我面临两个问题:-

  1. 我不能同时继承C类中的A、B类(C#不支持多重继承)
  2. 如果我使用接口而不是 A、B 类(在接口中我们不能包含字段)

【问题讨论】:

  • 您可以使用合成。还有mixins

标签: c# .net interface multiple-inheritance


【解决方案1】:

为什么不在 C 类中包含 A 类和 B 类的实例。使用Composition

class C
{
//class C properties
public A objA{get;set;}
public B objeB{get;set;}
}

然后就可以访问了

C objc = new C();
objc.objA.Property1 = "something";
objc.objB.Property1 = "something from b";

查看文章Composition vs Inheritance

编辑:

如果我使用接口而不是 A、B 类(在接口中我们不能 包含字段)

好吧,接口不能包含字段,如果你定义一个,你会得到编译错误。但接口可以包含属性,但您不能指定access specifiers 除外,因为接口的所有元素都被视为public。您可以将接口“A”和“B”的属性定义为:

public interface IA
{
     int Property1 { get; set; }
}


public interface IB
{
    int Property2 { get; set; }
}

然后您可以在 C 类中实现它们,例如:

public class C : IA, IB
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
}

稍后您可以将它们用作:

C objC = new C();
objC.Property1 = 0;
objC.Property1 = 0;

【讨论】:

  • 但是如果在C类中实现IA、IB是必要的,那么实现IA和IB需要什么?我们可以只定义属性而不实现接口...?
  • @UmarAbbas,这样做的唯一好处是在 IA 或 IB 中持有类 C 或任何其他实现接口 IAIB 的类的对象,并且具有多态行为.上面的代码只是一个例子,我还没有提供一个接口只有一堆属性而没有方法的真实场景。
  • 但是C类有它自己的属性。我们也可以在不实现 IA、IB 的情况下创建 C 类的对象。那么为什么我们用接口 IA, IB 来实现类 C 呢?
  • 如果我们在IA,IB中持有imp类的对象,它是如何多重继承的?为什么我们说它是多重继承?当我们还需要“多态”和“组合”时感谢您的回复! :)
  • @UmarAbbas,C# 中没有多重继承(我记得 Java)。上面的例子解释了接口是否可以有属性,并且有点试图通过实现两个接口来模拟多重继承。它不是纯粹的多重继承,在 C# 中没有办法进行多重继承
【解决方案2】:

接口可以具有属性,但如果您还想使用方法,则可能需要组合或依赖注入。

Interface A
{
   int PropA {get; set;}
}


Interface B
{
  int PropB {get; set;}
}

class C : A, B
{

}

// 将这些语句放在某个方法中

C c = new C();
c.PropA = 1;
c.PropB = 2;

【讨论】:

    【解决方案3】:

    接口不能解决缺乏多重继承的问题。他们只是不做同样的事情。你能得到的最接近的是使 C 成为 A 的子类,并具有 B 类型的属性。 也许如果您告诉我们 A、B 和 C 应该怎么做,我们可以给出更符合您需求的答案...

    【讨论】:

      【解决方案4】:

      接口可能包含属性,即:

      public interface IFoo
      {
          string Bar { get; set; }
      }
      

      【讨论】:

        【解决方案5】:

        考虑使用继承副组合时属性如何以不同的方式向客户端公开。

        继承:

        var myCclass= 新的 Cclass; myClass.propertyA; myClass.propertyB; myClass.propertyC; // 等等

        组成:

         var myCclass = new Cclass;
            myCclass.bClass.propertyB;
            myCclass.aClass.propertyA;
            myCclass.propertyC;
        

        继承提供了更简洁的 API - 一件好事。

        作文要求我对班级的内部结构有所了解——这不是一件好事。这违反了law of demeter——众所周知的最少知识原则。您可以通过让 Cclass 属性一对一地公开/返回 Bclass 和 Aclass 属性来解决此问题 - 然后您的 Bclass 和 Aclass 引用将在 Cclass 中是私有的或受保护的。并且 Cclass 可以完全控制公开的内容,而不是依赖 A 和 B 不公开您没有公开的内容。

        I agree with @AlejoBrz,接口在这里不合适。

        我也赞同“更喜欢组合而不是继承”。但这是一个指导方针,而不是硬性规定。

        【讨论】:

          【解决方案6】:
          public interface IAA
          {
              string NameOfA { get; set; }
          }
          public class AA : IAA
          {
              public string NameOfA{get;set;}
          }
          
          public interface IBB
          {
              string NameOfB { get; set; }
          }    
          public class BB : IBB
          {
              public string NameOfB{get;set;}
          }
          
          public class CC : IAA, IBB
          {
              private IAA a;
              private IBB b;            
          
              public CC()
              {
                  a = new AA{ NameOfA="a"};
                  b = new BB{ NameOfB="b"};
              }
          
              public string NameOfA{
                  get{
                      return this.a.NameOfA;
                     }
                  set{
                      this.a.NameOfA = value;
                     }
              }
          
              public string NameOfB
              {
                  get{
                      return this.b.NameOfB;
                  }
                  set{
                      this.b.NameOfB = value;
                  }
              }
          }
          

          【讨论】:

            【解决方案7】:

            接口不能包含字段,但可以包含属性。大多数情况下,属性可以像字段一样使用,不难说:

            接口 ISomeProperties {int prop1 {get;set;};字符串 prop2 {get;放;}} 接口 IMoreProperties {string prop3 {get;set;};双prop4 {get;放;}} 接口 ICombinedProperties : ISomeProperties, IMoreProperties; { }

            给定ICombinedProperties 类型的存储位置,可以直接访问所有四个属性,无需大惊小怪。

            但应该注意的是,有些事情可以用字段完成,而不能用属性完成。例如,虽然一个字段可以传递给Interlocked.Increment,但一个属性不能;尝试通过将属性复制到变量来Interlocked.Increment 属性,在其上调用Interlocked.Increment,然后将结果复制回属性在某些情况下可能“有效”,但如果两个线程尝试执行相同的操作,则会失败同时(例如,两个线程可以读取值 5,将其增加到 6,然后写回 6,而让两个线程在最初等于 5 的字段上调用 ​​Interlocked.Increment 将保证产生7.)。

            为了解决这个问题,可能需要让接口包含一些方法,这些方法可以在字段上执行互锁方法(例如,可以有一个函数在字段上调用 ​​Interlocked.Increment 并返回结果)和/或包含将调用指定委托的函数,并将字段作为ref 参数(例如

            委托 void ActionByRef(ref T1 p1); 委托 void ActionByRef(ref T1 p1, ref T2 p2); 委托 void ActionByRef(ref T1 p1, ref T2 p2, ref T3 p3); 接口 IThing { // 必须允许客户端代码直接使用 T 类型的字段。 void ActOnThing(ActionByRef proc); void ActOnThing(ActionByRef proc, ref ExtraT1 ExtraP1); 无效 ActOnThing (ActionByRef 过程,参考 ExtraT1 ExtraP1,参考 ExtraT2 ExtraP2); }

            给定接口的一个实例,可以执行以下操作:

            theInstance.ActOnThing( (ref int param) => Threading.Interlocked.Increment(ref param) );

            或者,如果有局部变量 maskValuexorValue 并希望使用 field = (field & maskValue) ^ xorValue 自动更新该字段:

            theInstance.ActOnThing( (ref int Param, ref int MaskValue, ref int XorValue) => { 诠释旧值,新值; 做 {oldValue = 参数; newValue = (oldValue & MaskValue) ^ XorValue; while (Threading.Interlocked.CompareExchange(ref Param, newValue, oldValue) != 旧值), 参考掩码值,参考异或值); );

            如果只想对字段执行几种类型的操作,最简单的方法是将它们简单地包含在界面中。另一方面,上面给出的方法允许接口以允许客户端对其执行任意序列的操作的方式公开其字段。

            【讨论】:

            • @radarbob:部分问题与接口不能包含字段这一事实有关。其他答案承认,在许多情况下,人们可能会使用属性而不是字段。我想强调这样一个事实,即即使人们想以属性不可替代的方式使用字段,也可以使用接口。
            猜你喜欢
            • 2013-02-07
            • 2011-09-05
            • 2022-08-12
            • 1970-01-01
            • 2013-11-25
            • 1970-01-01
            • 2013-03-31
            • 2011-03-02
            • 1970-01-01
            相关资源
            最近更新 更多