首页 文章

C#中的多重继承

提问于
浏览
194

由于多重继承是不好的(它使源更复杂),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 .


编辑

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

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

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

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

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

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

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

16 回答

  • 1

    我也喜欢这个 - 这是我个人所说的混合,虽然我意识到这是一个超载的术语 . 我希望能够指定用于实现接口的变量,并提供为特定方法提供自己的实现的选项 .

    我已经blogged about this in more detail - 虽然在故意夸大其继承方面的含义 .

    我认为没有理由不能在C#编译器中实现它 - 但它是另一种语言复杂性......

  • 0

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

  • 0

    是的使用Interface是一件麻烦事,因为无论何时我们在类中添加一个方法,我们都必须在界面中添加签名 . 另外,如果我们已经有一个带有一堆方法但没有接口的类呢?我们必须为我们想要继承的所有类手动创建Interface . 最糟糕的是,如果子类要从多个接口继承,我们必须在子类的Interfaces中实现所有方法 .

    通过遵循Facade设计模式,我们可以使用 accessors 模拟从多个类继承 . 在需要继承的类中使用{get; set;}将类声明为属性,并且所有公共属性和方法都来自该类,并且在子类的构造函数中实例化父类 .

    例如:

    namespace OOP
     {
         class Program
         {
             static void Main(string[] args)
             {
                 Child somechild = new Child();
                 somechild.DoHomeWork();
                 somechild.CheckingAround();
                 Console.ReadLine();
             }
         }
    
         public class Father 
         {
             public Father() { }
             public void Work()
             {
                 Console.WriteLine("working...");
             }
             public void Moonlight()
             {
                 Console.WriteLine("moonlighting...");
             }
         }
    
    
         public class Mother 
         {
             public Mother() { }
             public void Cook()
             {
                 Console.WriteLine("cooking...");
             }
             public void Clean()
             {
                 Console.WriteLine("cleaning...");
             }
         }
    
    
         public class Child 
         {
             public Father MyFather { get; set; }
             public Mother MyMother { get; set; }
    
             public Child()
             {
                 MyFather = new Father();
                 MyMother = new Mother();
             }
    
             public void GoToSchool()
             {
                 Console.WriteLine("go to school...");
             }
             public void DoHomeWork()
             {
                 Console.WriteLine("doing homework...");
             }
             public void CheckingAround()
             {
                 MyFather.Work();
                 MyMother.Cook();
             }
         }
    
    
     }
    

    使用此结构类Child可以访问Class Father和Mother的所有方法和属性,模拟多重继承,继承父类的实例 . 虽然不太一样但很实用 .

  • 14

    考虑只使用composition而不是尝试模拟多重继承 . 您可以使用接口来定义构成组合的类,例如: ISteerable 表示 SteeringWheel 类型的属性, IBrakable 表示 BrakePedal 类型的属性,等等 .

    完成后,您可以使用添加到C#3.0的Extension Methods功能来进一步简化这些隐含属性的调用方法,例如:

    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();
        }
    }
    
  • 0

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

    我 Build 在问题中提出的 IFirstISecondFirstSecondFirstAndSecond 方法的基础上 . 我将示例代码减少到 IFirst ,因为无论接口/ MI基类的数量如何,模式都保持不变 .

    让我们假设,MI FirstSecond 都将从相同的基类 BaseClass 派生,仅使用 BaseClass 中的公共接口元素

    这可以通过在 FirstSecond 实现中向 BaseClass 添加容器引用来表示:

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

    当引用 BaseClass 中的受保护接口元素或 FirstSecond 将成为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#允许嵌套类访问其包含的受保护/私有元素类,所以这可以用来链接 First 实现中的抽象位 .

    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的实际实现足够复杂并且访问的私有/受保护方法的数量适中,那么这种模式可能有助于克服缺少多重继承 .

  • 0

    我创建了一个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> { }
    

    您可以将后编译器作为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();
    

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

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

    我知道我知道即使它不被允许等等,有时你真的需要它,所以对于那些:

    class a {}
    class b : a {}
    class c : b {}
    

    就像在我的情况下我想做这个类b:Form(是的windows.forms)类c:b {}

    因为一半的功能是相同的,并且接口你必须重写它们

  • 4

    如果X继承自Y,那么它有两个有点正交的效果:

    • Y将为X提供默认功能,因此X的代码只需包含与Y不同的内容 .

    • 几乎任何Y都可以预期,可以使用X代替 .

    虽然继承提供了这两种功能,但不难想象在没有其他功能的情况下使用它们的情况 . 我所知道的没有.net语言有一种直接的方式来实现第一种没有第二种语言,虽然可以通过定义一个从不直接使用的基类来获得这样的功能,并且有一个或多个直接从它继承而没有添加任何东西的类new(这些类可以共享所有代码,但不能互相替换) . 但是,任何符合CLR的语言都允许使用提供接口的第二个特性(可替代性)而没有第一个(成员重用)的接口 .

  • 0

    如果你能忍受IFirst和ISecond的方法必须只与IFirst和ISecond的 Contract 相互作用的限制(就像在你的例子中那样)......你可以用扩展方法做你所要求的 . 实际上,这种情况很少发生 .

    public interface IFirst {}
    public interface ISecond {}
    
    public class FirstAndSecond : IFirst, ISecond
    {
    }
    
    public static MultipleInheritenceExtensions
    {
      public static void First(this IFirst theFirst)
      {
        Console.WriteLine("First");
      }
    
      public static void Second(this ISecond theSecond)
      {
        Console.WriteLine("Second");
      }
    }
    

    ///

    public void Test()
    {
      FirstAndSecond fas = new FirstAndSecond();
      fas.First();
      fas.Second();
    }
    

    所以基本的想法是你在接口中定义了所需的实现......这个必需的东西应该支持扩展方法中的灵活实现 . 无论何时需要“向接口添加方法”,您都可以添加扩展方法 .

  • 11

    这与Lawrence Wenham的回答一致,但根据您的使用情况,它可能会或可能不会有所改善 - 您不需要安装者 .

    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 .

  • -1

    MI并不坏,每个拥有(严重)使用它的人都喜欢它并且它不会使代码复杂化!至少不会比其他构造更复杂化代码 . 糟糕的代码是错误的代码,无论MI是否在图片中 .

    无论如何,我've got a nice little solution for Multiple Inheritance I wanted to share, it'在; http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog或者您可以按照我的信号中的链接... :)

  • 0

    在我自己的实现中,我发现使用MI的类/接口,虽然“良好的形式”,往往是一个巨大的过度复杂,因为你需要为少数必要的函数调用设置所有的多重继承,在我的情况下,需要冗长地完成数十次 .

    相反,简单地将不同模块化的静态"functions that call functions that call functions"作为一种OOP替换更容易 . 我正在研究的解决方案是"spell system"用于RPG,其中效果需要 heavily 混合和匹配函数调用以提供极端多样的法术而无需重新编写代码,就像示例似乎表明的那样 .

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

    编码似乎更快,更清洁这种方式IMO . 如果're just doing functions, and don' t需要继承 properties ,请使用函数 .

  • 107

    现在,通过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
    }
    
  • 0

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

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

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

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

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

  • 198

    多重继承是通常会导致比解决的问题更多的问题之一 . 在C语言中,它适合给你足够的绳索来悬挂自己的模式,但是Java和C#选择了更安全的路线,不给你选择 . 最大的问题是,如果继承多个具有与继承者未实现的签名相同的方法的类,该怎么办 . 它应该选择哪个类的方法?或者不应该编译?通常有另一种方法来实现大多数不依赖于多重继承的东西 .

  • 1

    我们似乎都在沿着这个接口路径前进,但是显而易见的另一种可能性就是做OOP应该做的事情,并 Build 你的继承树......(这不是类设计的全部关于?)

    class Program
    {
        static void Main(string[] args)
        {
            human me = new human();
            me.legs = 2;
            me.lfType = "Human";
            me.name = "Paul";
            Console.WriteLine(me.name);
        }
    }
    
    public abstract class lifeform
    {
        public string lfType { get; set; }
    }
    
    public abstract class mammal : lifeform 
    {
        public int legs { get; set; }
    }
    
    public class human : mammal
    {
        public string name { get; set; }
    }
    

    这个结构提供了可重用的代码块,当然,应该如何编写OOP代码?

    如果这种特殊方法不符合要求,我们只需根据所需对象创建新类...

    class Program
    {
        static void Main(string[] args)
        {
            fish shark = new fish();
            shark.size = "large";
            shark.lfType = "Fish";
            shark.name = "Jaws";
            Console.WriteLine(shark.name);
            human me = new human();
            me.legs = 2;
            me.lfType = "Human";
            me.name = "Paul";
            Console.WriteLine(me.name);
        }
    }
    
    public abstract class lifeform
    {
        public string lfType { get; set; }
    }
    
    public abstract class mammal : lifeform 
    {
        public int legs { get; set; }
    }
    
    public class human : mammal
    {
        public string name { get; set; }
    }
    
    public class aquatic : lifeform
    {
        public string size { get; set; }
    }
    
    public class fish : aquatic
    {
        public string name { get; set; }
    }
    

相关问题