【问题标题】:When should we use default interface method in C#?我们什么时候应该在 C# 中使用默认接口方法?
【发布时间】:2020-07-10 11:07:10
【问题描述】:

C# 8 及更高版本中,我们有 default interface methods,所以:

这不是破坏接口的原则吗?

我们什么时候应该使用默认接口方法而不是基础(抽象)类

【问题讨论】:

  • 默认接口方法背后的理念是可扩展性。假设您已经拥有一个代码库。现在,一个新功能请求需要您的一个接口包含一个新方法。如果没有默认的接口方法,您将很难过。现在,您可以添加新方法,在需要的地方实现它,并且仍然让预先存在的代码仍然可以正常工作。
  • 这个问题即使不是不可能,也很难在 Stack Overflow 上回答,因为它将基于意见。你问这是否是一个的想法,你问做其他事情是否会是一个更好的想法。最好先检查一下 SO 上的问题和答案是否已经涵盖了您的问题,如果没有,请重写它以减少基于意见的程度。
  • 有人问过这个问题(对于 Java,不是 C#,但原理实际上是相同的)on Software Engineering 并且有一些相对详细的答案(这样的理论/设计问题可能更适合 SE一般而言。)也有许多文章回答了这个问题,即 Red Hat 的博客,与 @Fildor 的评论类似,该博客指出“[它们] 提供了一种扩展接口的方法......而不会破坏以前的实现者”
  • 如果您检查 正确 标签的描述,您会找到解释和示例。不,它不会破坏接口,它与基类不同。 DIM 用于与已经使用默认方法的平台(如 Android SDK)进行版本控制、特征和互操作性

标签: c# interface c#-8.0 default-interface-member


【解决方案1】:

为什么我们有接口?

从理论上讲,接口实现和类继承都解决了同一个问题:它们允许您在类型之间定义subtype relationship

那么为什么我们在 C# 中同时拥有这两者?为什么我们需要接口?我们不能像在 C++ 中那样将接口定义为抽象类吗?

原因是the diamond problem(Image source)

如果BC 以不同的方式实现A.DoSomething()D 应该继承哪个实现?这是一个难题,Java 和 C# 设计者决定通过只允许不包含任何实现的特殊基类型的多重继承来避免它。他们决定将这些特殊的基本类型称为interfaces

因此,没有“接口原则”。接口只是解决特定问题的“工具”。

那么为什么我们需要默认实现呢?

向后兼容性。您编写了一个非常成功的库,全世界成千上万的开发人员都在使用它。您的库包含一些接口I,现在您决定在其上需要一个额外的方法M。问题是:

  • 您不能将另一个方法 M 添加到 I,因为这会破坏现有的实现 I 的类(因为它们没有实现 M),并且
  • 您不能将I 更改为抽象基类,因为这也会破坏实现I 的现有类,并且您将失去进行多重继承的能力。

那么默认实现如何避免菱形问题呢?

通过不继承这些默认方法(示例灵感来自 this article 中的方法,请参阅完整文章了解一些有趣的极端案例):

interface I1
{
    void M() { Console.WriteLine("I1.M"); } // default method
}

interface I2
{
    void M() { Console.WriteLine("I2.M"); } // default method
}

class C : I1, I2 { }

class Program
{
    static void Main(string[] args)
    {
        // c, i1 and i2 reference the same object
        C c = new C();
        I1 i1 = c;
        I2 i2 = c;

        i1.M(); // prints "I1.M"
        i2.M(); // prints "I2.M"
        c.M();  // compile error: class 'C' does not contain a member 'M'
    }
}

【讨论】:

    【解决方案2】:

    正如documentation 中所解释的,有两个主要应用程序:

    扩展现有 API

    虚拟扩展方法使 API 作者能够在未来版本中向接口添加方法,而不会破坏与该接口现有实现的源代码或二进制兼容性。

    因此,它旨在作为一种扩展 API(= 一组接口)的方法,而无需更新所有现有实现(因为它们需要实现新添加的功能)。

    traits pattern

    特征模式允许使用多组预先实现的方法来扩展一个类。这对于抽象类是不可能的:给定的类只能从单个父类继承,但可以从多个接口继承。

    请注意,diamond inheritance problem 附带此功能。因此,除了这些特定的应用程序之外,不应使用此功能来替换抽象类。

    【讨论】:

    • DIM 没有比普通接口更多的菱形继承问题。使用普通接口,显式接口实现避免了这个问题。 DIM 的调用方式与显式实现的接口相同
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 2010-12-30
    相关资源
    最近更新 更多