由于多重继承是不好的(它使源代码更加复杂),c#没有直接提供这样的模式。但有时候拥有这种能力是有帮助的。

例如,我能够使用接口和三个类实现缺少的多重继承模式:

public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }

public class First:IFirst 
{ 
    public void FirstMethod() { Console.WriteLine("First"); } 
}

public class Second:ISecond 
{ 
    public void SecondMethod() { Console.WriteLine("Second"); } 
}

public class FirstAndSecond: IFirst, ISecond
{
    First first = new First();
    Second second = new Second();
    public void FirstMethod() { first.FirstMethod(); }
    public void SecondMethod() { second.SecondMethod(); }
}

每当我向其中一个接口添加方法时,我也需要更改类FirstAndSecond。

是否有一种方法可以像在c++中那样将多个现有类注入到一个新类中?

也许有一种使用某种代码生成的解决方案?

或者它看起来像这样(虚构的c#语法):

public class FirstAndSecond: IFirst from First, ISecond from Second
{ }

这样,当我修改其中一个接口时,就不需要更新类FirstAndSecond。


EDIT

也许考虑一个实际的例子会更好:

您有一个现有的类(例如,基于ITextTcpClient的基于文本的TCP客户端),您已经在项目中的不同位置使用它。现在,您觉得有必要创建一个类的组件,以便windows窗体开发人员可以轻松访问。

据我所知,你目前有两种方法来做到这一点:

编写一个从组件继承的新类,并使用类本身的实例实现TextTcpClient类的接口,如FirstAndSecond所示。 编写一个继承TextTcpClient的新类,并以某种方式实现IComponent(实际上还没有尝试过这个)。

在这两种情况下,您都需要为每个方法而不是每个类做工作。因为你知道我们将需要TextTcpClient和Component的所有方法,所以将这两个方法合并到一个类中是最简单的解决方案。

为了避免冲突,这可以通过代码生成来完成,结果可以在之后修改,但手动输入这是一个纯粹的痛苦。


当前回答

考虑只使用组合而不是尝试模拟多重继承。你可以使用接口来定义组成组合的类,例如:isteable暗示了方向盘类型的属性,IBrakable暗示了BrakePedal类型的属性,等等。

一旦你完成了这些,你可以使用c# 3.0中添加的扩展方法功能来进一步简化对这些隐含属性的方法调用,例如:

public interface ISteerable { SteeringWheel wheel { get; set; } }

public interface IBrakable { BrakePedal brake { get; set; } }

public class Vehicle : ISteerable, IBrakable
{
    public SteeringWheel wheel { get; set; }

    public BrakePedal brake { get; set; }

    public Vehicle() { wheel = new SteeringWheel(); brake = new BrakePedal(); }
}

public static class SteeringExtensions
{
    public static void SteerLeft(this ISteerable vehicle)
    {
        vehicle.wheel.SteerLeft();
    }
}

public static class BrakeExtensions
{
    public static void Stop(this IBrakable vehicle)
    {
        vehicle.brake.ApplyUntilStop();
    }
}


public class Main
{
    Vehicle myCar = new Vehicle();

    public void main()
    {
        myCar.SteerLeft();
        myCar.Stop();
    }
}

其他回答

在我自己的实现中,我发现在MI中使用类/接口,虽然“形式很好”,但往往过于复杂,因为你需要为几个必要的函数调用设置所有的多重继承,在我的例子中,需要做几十倍的冗余。

相反,简单地在不同的模块化变体中创建静态的“调用调用函数的函数”作为一种面向对象的替代会更容易。我所致力于的解决方案是RPG的“法术系统”,即在不需要重新编写代码的情况下,效果需要大量混合和匹配函数调用来提供各种各样的法术,就像这个例子所表明的那样。

大多数函数现在都可以是静态的,因为我不必为拼写逻辑需要实例,而类继承在静态时甚至不能使用虚拟或抽象关键字。接口根本不能使用它们。

在我看来,这样编码更快更干净。如果只是使用函数,并且不需要继承属性,则使用函数。

由于多重继承(MI)的问题时常出现,我想添加一种方法来解决组合模式的一些问题。

我建立在IFirst, ISecond,First, Second, firststandsecond方法的基础上,就像问题中提到的那样。我将示例代码减少到IFirst,因为无论接口/ MI基类的数量如何,模式都保持不变。

让我们假设,MI First和MI Second都派生自同一个基类BaseClass,只使用BaseClass中的公共接口元素

这可以通过在第一个和第二个实现中向BaseClass添加容器引用来表示:

class First : IFirst {
  private BaseClass ContainerInstance;
  First(BaseClass container) { ContainerInstance = container; }
  public void FirstMethod() { Console.WriteLine("First"); ContainerInstance.DoStuff(); } 
}
...

当引用BaseClass中受保护的接口元素时,或者当First和Second是MI中的抽象类时,要求它们的子类实现一些抽象部分,事情就变得更加复杂了。

class BaseClass {
  protected void DoStuff();
}

abstract class First : IFirst {
  public void FirstMethod() { DoStuff(); DoSubClassStuff(); }
  protected abstract void DoStuff(); // base class reference in MI
  protected abstract void DoSubClassStuff(); // sub class responsibility
}

c#允许嵌套类访问其包含类的受保护/私有元素,因此这可以用于链接来自第一个实现的抽象位。

class FirstAndSecond : BaseClass, IFirst, ISecond {
  // link interface
  private class PartFirst : First {
    private FirstAndSecond ContainerInstance;
    public PartFirst(FirstAndSecond container) {
      ContainerInstance = container;
    }
    // forwarded references to emulate access as it would be with MI
    protected override void DoStuff() { ContainerInstance.DoStuff(); }
    protected override void DoSubClassStuff() { ContainerInstance.DoSubClassStuff(); }
  }
  private IFirst partFirstInstance; // composition object
  public FirstMethod() { partFirstInstance.FirstMethod(); } // forwarded implementation
  public FirstAndSecond() {
    partFirstInstance = new PartFirst(this); // composition in constructor
  }
  // same stuff for Second
  //...
  // implementation of DoSubClassStuff
  private void DoSubClassStuff() { Console.WriteLine("Private method accessed"); }
}

这里涉及到相当多的样板文件,但是如果FirstMethod和SecondMethod的实际实现足够复杂,并且访问的私有/受保护方法的数量适中,那么这种模式可能有助于克服缺乏多重继承的问题。

由于多重继承是不好的(它使源代码更加复杂),c#没有直接提供这样的模式。但有时候拥有这种能力是有帮助的。

c#和。net CLR还没有实现MI,因为他们还没有总结出它将如何在c#、VB.net和其他语言之间互操作,而不是因为“这会使源代码更复杂”。

MI是一个有用的概念,未回答的问题是:“当你在不同的超类中有多个公共基类时,你该怎么办?”

Perl是我使用过的唯一一种MI可以很好地工作的语言。. net可能有一天会引入它,但现在还没有,CLR已经支持MI,但正如我所说的,目前还没有针对它的语言结构。

在此之前,你会被代理对象和多个接口所困扰:(

考虑只使用组合而不是尝试模拟多重继承。你可以使用接口来定义组成组合的类,例如:isteable暗示了方向盘类型的属性,IBrakable暗示了BrakePedal类型的属性,等等。

一旦你完成了这些,你可以使用c# 3.0中添加的扩展方法功能来进一步简化对这些隐含属性的方法调用,例如:

public interface ISteerable { SteeringWheel wheel { get; set; } }

public interface IBrakable { BrakePedal brake { get; set; } }

public class Vehicle : ISteerable, IBrakable
{
    public SteeringWheel wheel { get; set; }

    public BrakePedal brake { get; set; }

    public Vehicle() { wheel = new SteeringWheel(); brake = new BrakePedal(); }
}

public static class SteeringExtensions
{
    public static void SteerLeft(this ISteerable vehicle)
    {
        vehicle.wheel.SteerLeft();
    }
}

public static class BrakeExtensions
{
    public static void Stop(this IBrakable vehicle)
    {
        vehicle.brake.ApplyUntilStop();
    }
}


public class Main
{
    Vehicle myCar = new Vehicle();

    public void main()
    {
        myCar.SteerLeft();
        myCar.Stop();
    }
}

这与Lawrence Wenham的回答相似,但取决于您的用例,这可能是一种改进,也可能不是——您不需要setter。

public interface IPerson {
  int GetAge();
  string GetName();
}

public interface IGetPerson {
  IPerson GetPerson();
}

public static class IGetPersonAdditions {
  public static int GetAgeViaPerson(this IGetPerson getPerson) { // I prefer to have the "ViaPerson" in the name in case the object has another Age property.
    IPerson person = getPerson.GetPersion();
    return person.GetAge();
  }
  public static string GetNameViaPerson(this IGetPerson getPerson) {
    return getPerson.GetPerson().GetName();
  }
}

public class Person: IPerson, IGetPerson {
  private int Age {get;set;}
  private string Name {get;set;}
  public IPerson GetPerson() {
    return this;
  }
  public int GetAge() {  return Age; }
  public string GetName() { return Name; }
}

现在,任何知道如何获取一个人的对象都可以实现IGetPerson,并且它将自动拥有GetAgeViaPerson()和GetNameViaPerson()方法。从这一点开始,基本上所有的Person代码都进入IGetPerson,而不是IPerson,除了新的ivars,它必须同时进入IGetPerson和IPerson。在使用这样的代码时,你不需要关心你的IGetPerson对象本身是不是一个IPerson。