由于多重继承是不好的(它使源代码更加复杂),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的所有方法,所以将这两个方法合并到一个类中是最简单的解决方案。

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


当前回答

我创建了一个c#后编译器来实现这类事情:

using NRoles;

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

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

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

public class FirstAndSecond : Does<RFirst>, Does<RSecond> { }

你可以将后编译器作为Visual Studio post-build-event运行:

C: \ some_path \ nroles-v0.1.0-bin \ nutate.exe " $(定位路径)"

在同一个程序集中,你可以这样使用它:

var fas = new FirstAndSecond();
fas.As<RFirst>().FirstMethod();
fas.As<RSecond>().SecondMethod();

在另一个程序集中,你像这样使用它:

var fas = new FirstAndSecond();
fas.FirstMethod();
fas.SecondMethod();

其他回答

如果X继承了Y,这有两个正交的效果:

Y将为X提供默认功能,因此X的代码只需要包含与Y不同的东西。 几乎在任何需要用Y的地方,都可以用X代替。

Although inheritance provides for both features, it is not hard to imagine circumstances where either could be of use without the other. No .net language I know of has a direct way of implementing the first without the second, though one could obtain such functionality by defining a base class which is never used directly, and having one or more classes that inherit directly from it without adding anything new (such classes could share all their code, but would not be substitutable for each other). Any CLR-compliant language, however, will allow the use of interfaces which provide the second feature of interfaces (substitutability) without the first (member reuse).

您可以有一个实现IFirst和ISecond的抽象基类,然后从该基类继承。

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

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

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

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

这与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。

在c# 8中,你实际上可以通过接口成员的默认实现来实现多重继承:

interface ILogger
{
    void Log(LogLevel level, string message);
    void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}

class ConsoleLogger : ILogger
{
    public void Log(LogLevel level, string message) { ... }
    // Log(Exception) gets default implementation
}