C# 当我们已经有了它的超集抽象类时,引入接口的必要性是什么?

C# 当我们已经有了它的超集抽象类时,引入接口的必要性是什么?,c#,oop,interface,abstract,C#,Oop,Interface,Abstract,抽象类包含两种类型的方法——抽象(未实现)方法和具体(实现)方法。而接口只包含未实现的方法。这意味着接口是抽象类的子集。那么为什么在C#(.Net)中引入接口呢?据我说,有两个原因: 支持多重继承 支持C#中值类型(结构)的继承 还有什么其他原因或隐藏的概念我没有考虑吗?你没有考虑的是这两个类之间的关系 继承(与抽象类一起使用)是一种is-a关系。因此,如果你正在为兽医诊所开发一个应用程序,你可能会创建一个动物抽象类,然后从中创建猫、狗、鸟和鱼,因为猫is-aAnimal,狗is-aAnimal

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

  • 支持多重继承
  • 支持C#中值类型(结构)的继承
    还有什么其他原因或隐藏的概念我没有考虑吗?

    你没有考虑的是这两个类之间的关系

    继承(与抽象类一起使用)是一种
    is-a
    关系。因此,如果你正在为兽医诊所开发一个应用程序,你可能会创建一个动物抽象类,然后从中创建猫、狗、鸟和鱼,因为猫
    is-a
    Animal,狗
    is-a
    Animal等等


    接口实现定义了一个
    可以做的
    关系。也许您希望能够在应用程序中打印一些内容(发票、动物、CustomerProfile)。您不应该为此使用继承(即抽象类),因为发票
    是-a
    打印没有任何意义,但是发票
    可以打印,客户档案
    可以打印也有意义。

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

    此外,接口通常被视为“更干净”与抽象类相比,抽象类具有一些设计优势。通过接口,所有成员都可以被覆盖,从而为希望提供自己实现的使用者提供最大的灵活性。通过抽象类,密封、抽象和虚拟方法的混合会使作为使用者的摸索变得困难。你不仅必须了解每个方法的API,但也需要了解默认实现是如何交互的。例如,假设您想要实现一个自定义集合类,该类在添加元素时打印每个元素。假设有一个包含2个方法的基类:

    virtual void Add(T item);
    virtual void AddRange(IEnumerable<T> items);
    
    但是,我们如何处理AddRange()?如果我们知道AddRange()的默认实现在引擎盖下调用Add(),那么我们根本不必重写它。另一方面,如果默认实现做了一些不同的事情(可能调用某些方法AddInternal(),它也被Add()调用),那么我们必须重写AddRange()以显式调用Add()


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

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

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

    但是“can act”并不意味着“应该”。
    对象[]
    可以充当
    列表
    ,或者我们可以使用
    委托
    来代替
    事件
    ,或者我们可以使用类型切换来代替泛型等,但是在C#中,在CLR中这将是错误的。但仍然可能

    所以回答你的问题-你不应该考虑形式上的差异,你应该考虑语义。在C语言中,接口被用来声明“它能做什么”


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

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

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

    接口是一种承诺契约中不存在任何实现的方式,并且引用此契约的组件在该契约的实现过程中不会受到任何更改。抽象类不能保证这一点,因为它们允许实现。即使您决定使用纯抽象类s、 其他一些开发人员可能会添加一些微小的实现作为一种快捷方式,因为它有助于完成任务。你怎么知道呢

    当然,有可能决定需要将一个新关键字应用于纯抽象类,以让编译器检查它是否确实是一个纯抽象类……接口概念又回来了

    这就是为什么接口概念需要存在于.Net中,而抽象类本身还不够的原因


    编辑添加:有趣的是,我一直在看,他在这一集中指出,java或C#等语言的接口是设计师不愿意/懒惰地解决问题的结果(他用致命的死亡三角来说明)。我可能一直在试图证明接口的合理性,因为它存在,而不是思考它存在的原因,但我仍然认为接口是一个有用的构造,可以保证没有代码
    override void Add(T item) { Console.WriteLine(item); base.Add(item); }