当我们已经有了接口的超集抽象类时,还有什么必要引入接口呢?

本文关键字:接口 什么 抽象类 我们 | 更新日期: 2023-09-27 17:54:42

抽象类包含两种类型的方法——抽象(非实现)和具体(实现)方法。而接口只包含未实现的方法。这意味着接口是抽象类的子集。那么为什么要在c# (.Net)中引入这些接口呢?在我看来,有两个原因:

  1. 支持多重继承
  2. 在c#中支持值类型(结构)的继承。

是否有其他原因或一些隐藏的概念,我错过了?

当我们已经有了接口的超集抽象类时,还有什么必要引入接口呢?

你忽略的是考虑两个类之间的关系。

继承(用于抽象类)是一个is-a关系。因此,如果你正在开发一个兽医诊所的应用程序,你可以创建一个Animal抽象类,然后从中创建Cat, Dog, Bird和Fish,因为Cat is-a Animal, Dog is-a Animal,等等。

接口实现定义了一个can-do关系。也许您希望能够在应用程序中打印一些东西(发票,动物,客户档案)。你不应该使用继承(即抽象类),因为Invoice is-a Print没有任何意义,但是Invoice can-do Print, CustomerProfile can-do Print确实有意义。

接口用于解耦应用程序中的组件;为了避免可能影响整个系统的关系,应用程序中必须存在一个分区,将其分为抽象组件和具体组件。

具体组件必须指向抽象组件,否则实现中的变化可能会影响整个体系结构。分区可能存在于应用程序的不同方面,但它们必须始终遵守这一原则,以确保应用程序不会变得脆弱。

Interface是一种承诺在合约中不存在实现的方式,并且引用该合约的组件不会受到该合约实现的更改的影响。抽象类不能保证这一点,因为它们允许实现。即使您决定使用纯抽象类,其他一些开发人员也可能会添加一些小的实现作为快捷方式,因为它有助于实现。你怎么知道的?

当然,有可能决定一个新的关键字需要应用到纯抽象类,让编译器检查它确实是一个纯抽象…界面概念又回来了!

这就是为什么接口概念需要在。net中存在的原因,以及为什么抽象类本身是不够的


编辑补充:有趣的是,我看了Robert Martin的"干净代码"系列的"接口隔离原则"一集,他在这一集中说,java或c#等语言的接口是设计师不愿意/懒惰解决多重继承问题的结果(他用死亡三角来说明)。我可能一直在试图证明接口的存在,而不是考虑它存在的原因,但我仍然认为接口是一个有用的结构,它保证了行为定义中没有代码。

您列出的原因都是有效的(接口并不是真正的多重继承,但是类确实可以实现多个接口)。从纯粹的功能角度来看,这些都是主要的优势,而且它们非常重要。

另外,接口通常被认为比抽象类更"干净",因此具有一些设计优势。有了接口,所有成员都可以被覆盖,从而为想要提供自己实现的消费者提供了最大的灵活性。对于抽象类,密封方法、抽象方法和虚拟方法的混合使用可能会使使用者难以理解。您不仅需要了解每个方法的API,还需要了解默认实现是如何交互的。例如,假设您想实现一个自定义集合类,它在添加元素时打印每个元素。假设有一个基类,它有两个方法:

virtual void Add(T item);
virtual void AddRange(IEnumerable<T> items);

我们首先将Add()重写为:

override void Add(T item) { Console.WriteLine(item); base.Add(item); }

但是,我们如何处理AddRange()呢?如果我们知道AddRange()的默认实现在底层调用Add(),那么我们根本不需要重写它。另一方面,如果默认实现做了一些不同的事情(可能调用一些方法AddInternal(),它也被Add()调用),那么我们必须重写AddRange()来显式调用Add()。

因此,强迫自己使用接口而不是抽象类可以使api更干净、更灵活。对于抽象类,每个密封方法都可能失去灵活性(并且通常可以被接口上的扩展方法所取代),并且每个虚拟方法都可能使消费者的事情变得更加复杂。

主要区别在于语义:接口声明行为契约("它能做什么?"),而类(包括抽象类)声明特定的实现("它是如何完成的?")。

有了这个,"纯抽象类",作为没有实现的类,确实可以充当接口(如果我们想象CLR支持多重继承)。

但是"can act"并不意味着"supposed to"。object[]可以充当List<int>,或者我们可以使用delegate代替event,或者我们可以使用按类型切换代替泛型等-但在c#中,在CLR中这将是错误的。但仍有可能。

回答你的问题,你不应该考虑形式差异,你应该考虑语义差异。在c#中,接口是用来声明"它能做什么"的。

你甚至可能看到完全空的接口——仅仅因为有人想"标记"任何实现这种空接口的东西来满足某人的需求——这是纯粹的语义和完全"合法"的接口用法。