抽象类包含两种类型的方法 - 抽象(未实现)方法以及具体(实现)方法。接口仅包含未实现的方法。它意味着接口是抽象类的子集。那么为什么接口是在C#(.Net)中引入的?据我所知,有两个原因:
我缺少任何其他原因或隐藏的概念吗?
答案 0 :(得分:7)
你缺少的是考虑两个班级之间的关系。
继承(与抽象类一起使用)是is-a
关系。因此,如果您正在为兽医诊所开发应用程序,您可以创建一个Animal抽象类,然后从中创建Cat,Dog,Bird和Fish,因为Cat is-a
Animal,Dog is-a
Animal等
接口实现定义了can-do
关系。也许您希望能够在应用程序中打印多个内容(Invoice,Animal,CustomerProfile)。您不应该使用继承(即抽象类),因为Invoice is-a
Print没有任何意义,但Invoice can-do
Print,CustomerProfile can-do
Print确实有意义。
答案 1 :(得分:1)
列出的原因都是有效的(接口不是真正的多重继承,但类可以实现多个接口)。从纯功能的角度来看,这些是主要优势,而且它们非常重要。
此外,接口通常被视为比抽象类“更清晰”,因此具有多个设计优势。通过界面,可以覆盖所有成员,从而为希望提供自己的实现的消费者提供最大的灵活性。对于抽象类,密封,抽象和虚拟方法的混合使得很难成为消费者。您不仅需要了解每种方法的API,还需要了解默认实现的交互方式。例如,假设您想要实现一个自定义集合类,它会在添加元素时打印它们。想象一下,有一个基类有2种方法:
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更清晰,更灵活。对于抽象类,每个密封方法都可能失去灵活性(并且通常可以被接口上的扩展方法替换),并且每个虚拟方法都可以使消费者的事情变得更加复杂。
答案 2 :(得分:1)
主要区别在于语义:接口声明行为契约(&#34;它能做什么?&#34;),而类(包括抽象的)声明特定的实现(&#34;它是怎样的完成&#34;?)。
拥有这个&#34;纯抽象类&#34;,作为没有实现的类,确实可以充当接口(如果我们想象CLR支持多重继承)。
但是&#34;可以行动&#34;并不意味着&#34;应该&#34;。 object[]
可以充当List<int>
,或者我们可以使用delegate
代替event
,或者我们可以使用按类型切换而不是泛型等 - 但在C#中,在CLR中它将是错了。但仍有可能。
所以回答你的问题 - 你不应该考虑形式上的差异,你应该考虑语义。在C#中,接口用于声明&#34;它能做什么&#34;。
你甚至可能会看到完全空的界面 - 只是因为有人想要&#34;标记&#34;任何实现这样的空界面以满足某人需要的东西 - 这纯粹是语义的,完全的,合法的&#34;界面的用法。
答案 3 :(得分:1)
接口用于解耦应用程序中的组件;为了避免可能影响整个系统的关系,应用程序中必须存在一个分区,将其分为抽象和具体组件。
具体组件必须指向抽象组件,否则实现中的更改可能会影响整个架构。分区可能存在于应用程序的不同方面,但它们必须始终遵循此原则以确保应用程序不会变得脆弱。
接口是一种承诺合同中不存在实施的方式,并且引用此合同的组件不会受到本合同实施方式的更改。抽象类无法实现此保证,因为它们允许实现。即使您决定使用纯抽象类,其他一些开发人员也可能会添加一些微小的实现作为快捷方式,因为它有助于降低这一点。你怎么能说出来?
当然可以决定是否需要将新关键字应用于纯抽象类,让编译器检查它确实是一个纯粹的抽象......并且界面概念又回来了!
这就是为什么界面概念需要存在于.Net中以及为什么抽象类本身不够的原因
编辑添加:有趣的是,我正在观看the Interface Segregation Principle episode of the "Clean Code" series by Robert Martin,他在本集中指出,java或C#等语言的界面是设计师不愿意/懒惰解决问题的结果multiple inheritance(他用死亡的死亡三角形说明)。我可能一直试图证明界面的合理性,因为它存在而不是考虑它存在的原因,但我仍然认为界面是一个有用的结构,可以保证行为定义中缺少代码。