【问题标题】:How did C#'s lack of multiple inheritance lead to the need for interfaces?C# 缺乏多重继承是如何导致需要接口的?
【发布时间】:2013-01-07 02:17:51
【问题描述】:

The C# Programming LanguageKrzysztof Cwalina 在注释中声明:

我们明确决定不添加对多重继承的支持 [...] 缺乏多重继承迫使我们添加了 接口,这反过来又负责与问题 框架的演变,更深的继承层次结构,以及许多 其他问题。

接口是面向对象编程语言的核心概念。我不遵循“强迫我们添加接口的概念”的意思

Krzysztof 是否意味着必须针对接口的使用做出某些设计决策,否则将使用多重继承?或者,他的意思是 interface 是因为缺少多重继承而被引入 C# 的?可以举个例子吗?

【问题讨论】:

  • 我认为引入它们是为了解决缺乏多重继承的问题,因为没有它们,许多模式将难以采用。
  • 这更适合程序员,因为这不是一个实际问题。
  • 我不认为接口是面向对象编程的基础。你做的很有趣。
  • @ToolmakerSteve:我也不认为类继承是面向对象的基础。接口和类继承都是名义子类型的特殊情况,这也不是面向对象的基础。这样想:有没有没有类继承的面向对象语言?当然。有没有没有接口的 OO 语言?当然。所以两者都不是基本的。
  • @EricLippert - 谢谢,这对我来说更有意义。鉴于上下文是关于类继承的讨论,我认为您是在建议类继承相对于 OO 更为基本。考虑到这一点,我同意接口与 OO 正交——它们是一种契约形式。

标签: c# oop interface multiple-inheritance


【解决方案1】:

接口只是一个没有数据成员并且只定义public abstract 方法的基类。例如,这将是 C++ 中的接口:

class IFrobbable {
    public:
    virtual void Frob() = 0;
}

因此,当 MI 可用作语言功能时,您可以通过简单地从接口派生来“实现”接口(同样是 C++):

class Widget : public IFrobbable, public IBrappable {
    // ...
}

多重继承在一般情况下会引发许多问题和问题,这些问题不一定有一个单一的答案,甚至对于你对“好”的特定定义(dreaded diamond , 任何人?)。多个接口实现回避了大部分这些问题,因为“继承”接口的概念是继承成熟类的一个非常受限的特殊情况。

这就是“迫使我们添加接口的概念”的地方:当仅限于单一继承时,您无法进行太多的 OO 设计,例如,当代码重用时,无法重用代码会出现严重问题实际上是 OO 最常见的论点之一。你必须做更多的事情,下一步是添加多重继承,但仅限于满足接口约束的类。

所以,我将 Krzysztof 的引述解释为:

一般情况下的多重继承是一个很棘手的问题 鉴于现实生活,我们无法以令人满意的方式解决 .NET 发展的制约因素。但是接口继承 都是很多 在 OOP 中更容易处理和最重要的,所以我们确实把它 但当然接口也有自己的问题, 主要是关于 BCL 的结构。

【讨论】:

  • 我觉得最好说每个接口都是类中的一个vtable。更容易解释非虚拟成员如何实现接口或基成员如何在派生类中实现接口。 (同时指出实现接口与继承有很大不同)
  • @adrianm:vtable 是一个实现细节,如果它无处不在的话。我更愿意坚持抽象概念。
  • 嗯,你忘了还提到 Java 已经证明接口继承适用于大多数市场。
  • @benham 实际上,接口继承已经存在,并且在 Java 首次向公众发布之前就已经在商业上取得了成功并在市场上得到了证明。
【解决方案2】:

来自Chris Brumme

我们不实施 Multiple 的原因有很多 直接实现继承。 (如您所知,我们支持多个 接口继承)。

我认为 Krzysztof Cwalina 在引用中所说的不是接口本身的概念,而是作为多重继承方法的多重接口继承。

我们没有提供内置的、可验证的、 多实现继承的 CLS 兼容版本:

  1. 不同的语言实际上对 MI 的工作方式有不同的期望。例如,如何解决冲突以及是否重复 碱基被合并或冗余。在我们可以在 CLR 中实现 MI 之前, 我们必须对所有语言进行调查,找出共同点 概念,并决定如何以中立语言的方式表达它们。 我们还必须决定 MI 是否属于 CLS 以及什么 这意味着对于不想要这个概念的语言(大概 例如 VB.NET)。当然,这就是我们所从事的业务 公共语言运行时,但我们还没有为 MI 做这件事 还没有。

  2. 真正适合 MI 的地方实际上很少。在很多情况下,多接口继承可以获得 而是完成了工作。在其他情况下,您也许可以使用封装 和代表团。如果我们要添加一个稍微不同的结构,比如 mixins,真的会更强大吗?

  3. 多重实现继承给实现注入了很多复杂性。这种复杂性会影响铸造、布局、 调度、字段访问、序列化、身份比较, 可验证性、反射、泛型,可能还有很多其他的 地点。

【讨论】:

  • 比这些问题更根本的是,在菱形场景中,从公共基础派生的中间层类必须共享相同的基类实例或使用单独的基类实例。共享基类实例将使派生类难以或不可能强制执行其不变量。使用单独的实例将使保持身份的向上转换和向下转换成为不可能(因为与引用关联的基类实例将取决于对其执行的转换序列)。换句话说,.NET 从根本上违反了 .NET 公理。
【解决方案3】:

我认为你应该 read what Eric Lippert says about Interfaces 。他的手很脏,所以我认为他比其他人都清楚。

有时会有更坏的情况和最坏的情况。你必须选择不太糟糕的那个。

以下是链接帖子的副本:


他们只是为了确保上述功能(在 接口)在继承类中实现。

正确。这是一个足够令人敬畏的好处来证明该功能的合理性。正如其他人所说,接口是实现某些方法、属性和事件的合同义务。静态类型语言的显着优势是编译器可以验证您的代码所依赖的契约是否真正得到满足。

也就是说,接口是表示合同义务的一种相当薄弱的方式。如果您想要一种更强大、更灵活的方式来表示合同义务,请查看 Visual Studio 最新版本附带的代码合同功能。

C# 是一门很棒的语言,但有时它会让你觉得 首先微软制造了问题(不允许多个 继承),然后提供解决方案,这是相当 乏味的。

很高兴你喜欢它。

所有复杂的软件设计都是权衡相互冲突的功能,并试图找到以小成本带来大收益的“最佳位置”。我们从痛苦的经历中了解到,为了实现共享而允许多重继承的语言具有相对较小的好处和相对较大的成本。仅在不共享实现细节的接口上允许多重继承,可以在不增加大部分成本的情况下提供多重继承的许多好处。

【讨论】:

  • @AppDeveloper 已修复。它没有正确标记,但仍然是一个有效的链接。
  • 您可能不应该复制整个帖子。最好选择一些选择摘要。
  • 接口不仅仅是为了确保特定类包含一个函数。它们更重要的目的是定义可替换关系:方法可以要求“实现IEnumerable<Frob> 的东西”,并且编译器将允许替换其他类型当且仅当它们实现了该接口。
【解决方案4】:

我认为 Cwalina 的语言有点强,关于历史并不完全准确。

请记住,接口在 C# 之前就已经存在,所以说“我们必须添加接口来解决问题 x”对我来说听起来不对。我认为默认情况下接口会存在 - 它们必须存在。

另外,请记住,C# 很大程度上源自 C++。或者,至少,参与创建 C# 语言的人在 C++ 方面具有很强的背景。多重继承是 C++ 中公认的噩梦领域。从多个类继承,这些类本身可能从 commonm 基类派生,确定哪个实现优先等。我的意思是,这是一个非常强大的功能,但无疑会导致复杂的代码。

我怀疑他们放弃了 C# 的多重继承,因为他们(语言作者)知道一切都会变得多么复杂,并且他们想避免这种情况。还要记住,当 C# 被引入时,它的卖点之一就是它的简洁性(绝对不比 Java 干净,但比 C 和 C++ 干净得多)。

顺便说一句,在 10 多年的 C# 开发中,我只希望多重继承一次。我想继承视觉风格和一些常见行为的 UI 组件。没过多久我就意识到我打算做的无论如何都是一个非常糟糕的设计。

【讨论】:

    猜你喜欢
    • 2011-09-16
    • 1970-01-01
    • 2022-08-12
    • 2013-02-07
    • 1970-01-01
    • 2017-07-18
    • 1970-01-01
    • 2013-03-31
    • 2012-05-13
    相关资源
    最近更新 更多