C#中的多重继承

Mar*_*tin 207 c# interface multiple-inheritance

由于多重继承是不好的(它使源更复杂),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(); }
}
Run Code Online (Sandbox Code Playgroud)

每次我向其中一个接口添加方法时,我都需要更改类FirstAndSecond.

有没有办法将多个现有类注入一个新类,就像在C++中一样?

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

或者它可能看起来像这样(假想的c#语法):

public class FirstAndSecond: IFirst from First, ISecond from Second
{ }
Run Code Online (Sandbox Code Playgroud)

因此,当我修改其中一个接口时,不需要更新类FirstAndSecond.


编辑

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

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

据我所知,您目前有两种方法可以做到这一点:

  1. 编写一个继承自组件的新类,并使用类本身的实例实现TextTcpClient类的接口,如FirstAndSecond所示.

  2. 编写一个继承自TextTcpClient的新类,并以某种方式实现IComponent(尚未实际尝试过).

在这两种情况下,您都需要按方法而不是按类进行工作.既然你知道我们需要TextTcpClient和Component的所有方法,那么将这两个方法组合成一个类就是最简单的解决方案.

为了避免冲突,这可以通过代码生成来完成,其中结果可以在之后改变,但是手动键入它是屁股中的纯粹痛苦.

Chr*_*ham 205

考虑只使用组合而不是尝试模拟多重继承.您可以使用接口来定义构成合成的类,例如:ISteerable隐含类型属性SteeringWheel,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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这就是重点 - 像这样的想法可以减轻构图. (9认同)
  • 是的,但是在某些用例中您确实需要将这些方法作为主要对象的一部分 (8认同)
  • 不幸的是,在扩展方法中无法访问成员变量数据,因此您必须将它们公开为内部或(ug)public,尽管我认为按合同组合是解决多重继承的最佳方法. (7认同)
  • 很好的答案!简洁,易懂,非常有用的插图.谢谢! (4认同)
  • 我们可能想在调用“停止”之前检查“ myCar”是否已完成向左转向。如果myCar速度过快时应用Stop,则可能会翻转。:D (2认同)
  • 很好的答案。我知道这是一篇较旧的帖子,但我发现编写一个结合多个字段的专门类存在问题。IDisposable 模式也是一个挑战。也许是另一篇文章的主题。 (2认同)

Ian*_*ton 120

由于多重继承是不好的(它使源更复杂),C#不直接提供这样的模式.但有时候拥有这种能力会有所帮助.

C#和.net CLR还没有实现MI,因为他们尚未总结如何在C#,VB.net和其他语言之间进行互操作,而不是因为"它会使源更复杂"

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

Perl是我工作过的唯一一种MI工作和运作良好的语言..Net可能有一天会介绍它但是还没有,CLR确实已经支持MI,但正如我所说,除此之外还没有语言结构.

在此之前,你会遇到代理对象和多个接口:(

  • CLR不支持多实现继承,只支持多接口继承(C#也支持). (38认同)
  • @MandeepJanjua我没有声称任何这样的事情,我说'很可能会介绍它'事实仍然是ECMA标准CLR确实提供了IL机制的多重继承,只是没有充分利用它. (9认同)
  • @Jordão:为了完整起见:编译器可以为CLR中的类型创建MI.它确实有它的警告,例如它不符合CLS.有关更多信息,请参阅此(2004)文章http://blogs.msdn.com/b/csharpfaq/archive/2004/03/07/why-doesn-tc-support-multiple-inheritance.aspx (3认同)
  • @MrHappy:非常有趣的文章.我实际上已经研究过C#的某种[Trait Composition](http://codecrafter.blogspot.com/2011/05/nroles-experiment-with-roles-in-c.html),看一看. (2认同)
  • FYI多重继承也不错,并且不会使代码变得复杂.我以为我会提到它. (2认同)

Jor*_*dão 14

我创建了一个C#post-compiler来支持这种事情:

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> { }
Run Code Online (Sandbox Code Playgroud)

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

C:\ some_path \nroles-v0.1.0-bin \nutate.exe"$(TargetPath)"

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

var fas = new FirstAndSecond();
fas.As<RFirst>().FirstMethod();
fas.As<RSecond>().SecondMethod();
Run Code Online (Sandbox Code Playgroud)

在另一个程序集中,您可以像这样使用它

var fas = new FirstAndSecond();
fas.FirstMethod();
fas.SecondMethod();
Run Code Online (Sandbox Code Playgroud)


Joe*_*orn 5

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

  • @leppie-“每次我向其中一个接口添加一个方法时,我也需要更改类FirstAndSecond。” 此解决方案无法解决原始问题的这一部分,对吗? (2认同)
  • 您将不得不编辑抽象类,但是不必编辑依赖于它的任何其他类。责任制止于此,而不是继续级联到整个班级。 (2认同)
  • @JoelCoehoorn:我不遵循这个答案......类可以实现多个接口,那么为什么还需要一个抽象基类呢?我不明白这将如何解决 MI 问题? (2认同)

Lud*_*kov 5

现在,使用 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
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,但请注意,在上面,您将无法执行 `new ConsoleLogger().Log(someEception)` — 它根本不起作用,您必须将对象显式转换为 `ILogger` 才能使用默认的接口方法。所以它的用处有些有限。 (9认同)