由于多重继承是不好的(它使源更复杂),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 回答
我也喜欢这个 - 这是我个人所说的混合,虽然我意识到这是一个超载的术语 . 我希望能够指定用于实现接口的变量,并提供为特定方法提供自己的实现的选项 .
我已经blogged about this in more detail - 虽然在故意夸大其继承方面的含义 .
我认为没有理由不能在C#编译器中实现它 - 但它是另一种语言复杂性......
您可以拥有一个实现IFirst和ISecond的抽象基类,然后从该基类继承 .
是的使用Interface是一件麻烦事,因为无论何时我们在类中添加一个方法,我们都必须在界面中添加签名 . 另外,如果我们已经有一个带有一堆方法但没有接口的类呢?我们必须为我们想要继承的所有类手动创建Interface . 最糟糕的是,如果子类要从多个接口继承,我们必须在子类的Interfaces中实现所有方法 .
通过遵循Facade设计模式,我们可以使用 accessors 模拟从多个类继承 . 在需要继承的类中使用{get; set;}将类声明为属性,并且所有公共属性和方法都来自该类,并且在子类的构造函数中实例化父类 .
例如:
使用此结构类Child可以访问Class Father和Mother的所有方法和属性,模拟多重继承,继承父类的实例 . 虽然不太一样但很实用 .
考虑只使用composition而不是尝试模拟多重继承 . 您可以使用接口来定义构成组合的类,例如:
ISteerable
表示SteeringWheel
类型的属性,IBrakable
表示BrakePedal
类型的属性,等等 .完成后,您可以使用添加到C#3.0的Extension Methods功能来进一步简化这些隐含属性的调用方法,例如:
由于多重继承(MI)的问题不时出现,我想添加一种方法来解决组合模式的一些问题 .
我 Build 在问题中提出的
IFirst
,ISecond
,First
,Second
,FirstAndSecond
方法的基础上 . 我将示例代码减少到IFirst
,因为无论接口/ MI基类的数量如何,模式都保持不变 .让我们假设,MI
First
和Second
都将从相同的基类BaseClass
派生,仅使用BaseClass
中的公共接口元素这可以通过在
First
和Second
实现中向BaseClass
添加容器引用来表示:当引用
BaseClass
中的受保护接口元素或First
和Second
将成为MI中的抽象类时,事情变得更加复杂,需要它们的子类实现一些抽象部分 .C#允许嵌套类访问其包含的受保护/私有元素类,所以这可以用来链接
First
实现中的抽象位 .涉及相当多的样板,但如果FirstMethod和SecondMethod的实际实现足够复杂并且访问的私有/受保护方法的数量适中,那么这种模式可能有助于克服缺少多重继承 .
我创建了一个C# post-compiler来启用这种东西:
您可以将后编译器作为Visual Studio post-build-event运行:
在同一个程序集中,您可以像这样使用它:
在另一个程序集中,您可以像这样使用它
我知道我知道即使它不被允许等等,有时你真的需要它,所以对于那些:
就像在我的情况下我想做这个类b:Form(是的windows.forms)类c:b {}
因为一半的功能是相同的,并且接口你必须重写它们
如果X继承自Y,那么它有两个有点正交的效果:
Y将为X提供默认功能,因此X的代码只需包含与Y不同的内容 .
几乎任何Y都可以预期,可以使用X代替 .
虽然继承提供了这两种功能,但不难想象在没有其他功能的情况下使用它们的情况 . 我所知道的没有.net语言有一种直接的方式来实现第一种没有第二种语言,虽然可以通过定义一个从不直接使用的基类来获得这样的功能,并且有一个或多个直接从它继承而没有添加任何东西的类new(这些类可以共享所有代码,但不能互相替换) . 但是,任何符合CLR的语言都允许使用提供接口的第二个特性(可替代性)而没有第一个(成员重用)的接口 .
如果你能忍受IFirst和ISecond的方法必须只与IFirst和ISecond的 Contract 相互作用的限制(就像在你的例子中那样)......你可以用扩展方法做你所要求的 . 实际上,这种情况很少发生 .
///
所以基本的想法是你在接口中定义了所需的实现......这个必需的东西应该支持扩展方法中的灵活实现 . 无论何时需要“向接口添加方法”,您都可以添加扩展方法 .
这与Lawrence Wenham的回答一致,但根据您的使用情况,它可能会或可能不会有所改善 - 您不需要安装者 .
现在任何知道如何获取人的对象都可以实现IGetPerson,它将自动拥有GetAgeViaPerson()和GetNameViaPerson()方法 . 从这一点来看,基本上所有Person代码都进入IGetPerson,而不是进入IPerson,而不是新的ivars,它们必须同时进入两者 . 在使用这样的代码时,您不必担心您的IGetPerson对象本身是否真的是IPerson .
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或者您可以按照我的信号中的链接... :)
在我自己的实现中,我发现使用MI的类/接口,虽然“良好的形式”,往往是一个巨大的过度复杂,因为你需要为少数必要的函数调用设置所有的多重继承,在我的情况下,需要冗长地完成数十次 .
相反,简单地将不同模块化的静态"functions that call functions that call functions"作为一种OOP替换更容易 . 我正在研究的解决方案是"spell system"用于RPG,其中效果需要 heavily 混合和匹配函数调用以提供极端多样的法术而无需重新编写代码,就像示例似乎表明的那样 .
大多数函数现在都可以是静态的,因为我不一定需要一个拼写逻辑实例,而类继承甚至不能使用静态的虚拟或抽象关键字 . 接口根本不能使用它们 .
编码似乎更快,更清洁这种方式IMO . 如果're just doing functions, and don' t需要继承 properties ,请使用函数 .
现在,通过C#8,您可以通过接口成员的默认实现实现多重继承:
C#和.net CLR还没有实现MI,因为他们还没有总结如何在C#,VB.net和其他语言之间进行交互,而不是因为“它会使源更复杂”
MI是一个有用的概念,未回答的问题是: - “你做什么?在不同的超类中有多个公共基类?
Perl是我曾经使用的唯一语言,MI工作和运作良好 . .Net可能有一天会介绍它但是还没有,CLR确实已经支持MI了,但正如我所说的那样,除此之外还没有语言结构 .
在此之前,你会遇到代理对象和多个接口:(
多重继承是通常会导致比解决的问题更多的问题之一 . 在C语言中,它适合给你足够的绳索来悬挂自己的模式,但是Java和C#选择了更安全的路线,不给你选择 . 最大的问题是,如果继承多个具有与继承者未实现的签名相同的方法的类,该怎么办 . 它应该选择哪个类的方法?或者不应该编译?通常有另一种方法来实现大多数不依赖于多重继承的东西 .
我们似乎都在沿着这个接口路径前进,但是显而易见的另一种可能性就是做OOP应该做的事情,并 Build 你的继承树......(这不是类设计的全部关于?)
这个结构提供了可重用的代码块,当然,应该如何编写OOP代码?
如果这种特殊方法不符合要求,我们只需根据所需对象创建新类...