为什么C#允许通过接口扩展方法而不是类来进行多重inheritance?

我已经检查了其他问题,但令人惊讶的是,这个问题似乎没有被问到。 使用Extension方法,接口提供有限但真正的实现多重inheritance。 这带来了Diamond问题,与基于类的多inheritance相同。 为什么这比基于类的多重inheritance更好或更可接受,这么多人似乎觉得这么可怕? 它实际上似乎是一种更糟糕的实现多重inheritance的方式,因为扩展方法不能进入接口本身,甚至不能进入实现接口的类,但最终可能分散在多个静态实用程序类中。

Eric Lippert在他的博客(2009年10月5日上午9:29)似乎对扩展属性的概念持开放态度,甚至提到了扩展事件,扩展操作符,扩展构造函数(也称为“工厂模式”)的可能性。 因此可以进一步扩展通过接口的实现。

编辑:为了阐明一个类是否inheritance了两个接口,这两个接口都具有相同名称和类型参数的扩展方法,如果调用方法而不显式命名接口,则会产生编译错误。 考虑到这一点,我错了,因为这不是钻石问题。 然而,考虑到这一点提出了一个问题,即钻石问题与其他歧义相比有多重要? 为什么Diamond问题难以解决,它是否可以通过简单的编译错误来获取,就像接口扩展方法类冲突并且不能隐式解析一样? 即使在基于类的多重inheritance中,也可能存在非基于Diamond的成员签名冲突。

使用扩展方法,接口提供有限但真正的实现多重inheritance。

这句话是整个问题的基础,但我无法做出头脑或尾巴,因此很难回答这个问题。

首先,让我们清楚地定义“inheritance”。 当我们说类型Dinheritance自类型B时,我们的意思是B的每个成员也是D†的成员 。 这就是我们所说的C#中的“inheritance”。

类(或结构)从一个(††)基类inheritance成员。 一个类可以实现任意数量的接口; 这与基类inheritance完全不同。 类不需要具有与其实现的接口相同的成员集,因为该类可以使用显式接口实现来提供实现而无需成为类的成员。 显式接口实现只能通过接口访问,并且不能以任何其他方式访问,因此将其视为从接口“inheritance”的类的“成员”会很奇怪。

接口从任意数量的其他接口“inheritance”成员。 从技术上讲,这可以被认为是inheritance; 基接口的成员是派生接口的成员。 但是我希望我们没有在规范中描述它; 我认为说接口不从基接口inheritance会更清楚; 相反,接口可能需要实现其他接口作为其合同的一部分。

既然我们已经解决了这个问题,那么扩展方法呢? 扩展方法不是任何类型的inheritance; 扩展的类型不会获得任何新成员。 扩展方法只是一种更愉快地编写静态方法调用的方法。

这带来了Diamond问题,与基于类的多inheritance相同

目前还不清楚该句中“这个”是指什么。 你指的是(1)实现多个接口的类,(2)从多个接口inheritance的接口,或者(3)关于扩展方法的东西,或者(4)完全不同的东西? 我不明白钻石问题与你的问题有什么关系。 你能澄清一下吗?

为什么这比基于类的多重inheritance更好或更可接受,这么多人似乎觉得这么可怕?

为什么更好?

我根本不理解这个问题,但似乎在某处有某种有用的问题。 你能澄清一下这个问题吗? 最好用一些简短的示例代码来演示您正在谈论的内容。


†不是每个成员。 例如,构造函数和析构函数是成员,但不是可inheritance的成员。 私有成员inheritance的,但可能无法通过名称访问

††除了inheritance自零类的object之外。 每个其他类都只从一个类inheritance。

任何实现多重inheritance的扩展方法的出现完全是一种错觉。 他们不。

扩展方法是简单的编译器技巧。 它们编译为普通的旧静态方法,它们看起来和工作方式就像从第一个参数中删除它一样。

考虑:

 myObj.Extension(); ... public static class MyExtension { public static void Extension(this MyObj myobj) 

调用扩展名相当于:

 MyExtension.Extension(myObj); 

当然,您甚至可以在代码中将其称为。

C#类实现的接口列表是扁平的 ,因此当一个类通过它实现的多个接口inheritance它来实现接口时,该类需要提供的实现数量仍然是一个。

例如,如果一个类实现了两个接口,这两个接口都inheritance自IDisposable ,那么该类仍然只需要实现一次Dispose() 。 这与C ++形成对比,在C ++中,需要分别重写通过多个非虚拟inheritance路径从同一基类inheritance的函数。

扩展方法与此问题正交,因为它们提供的实现无法覆盖。 我写了一篇关于扩展方法的博客文章及其在“横向”共享实现中的作用。 我认为它们是一种以完全独立于通过类inheritance获得的“垂直”实现共享的方式提供function的机制。

扩展方法只是美化的静态方法,在调用看起来像实例方法(如果调用者选择的话)。

您所描述的情况不会发生,因为编译器会将调用标记为不明确:

 interface I1 { } interface I2 { } class C : I1, I2 { } static class Ex1 { public static void M(this I1 self) { } } static class Ex2 { public static void M(this I2 self) { } } ... new C().M(); // ERROR: The call is ambiguous 

只有在当前上下文中using扩展方法导入包含静态类的命名空间(通过using指令)时,扩展方法才有效; 或者如果您的声明与它们位于相同的命名空间中。 因此,即使您可以创建不明确的声明,如果将它们添加到不同的命名空间,那么调用者只能通过导入所需的命名空间来消除歧义。

另外,要消除歧义,调用者可以将该方法作为常规静态方法调用:

 Ex1.M(new C()); // not ambiguous anymore 

或者你可以转换到适当的界面:

 ((I1)new C()).M(); // not ambiguous anymore 

所以,并不是说你“inheritance”必须在声明时解决的冲突成员,你可以随意使用,而且你必须告诉编译器你想在呼叫时使用哪一个。

旁注 :我发现这种扩展接口的能力是一种有趣的方式,可以在C#中创建一个mixinforms。 我以前写过这篇文章,比如说。