C# 8 默认实现和依赖倒置

Jos*_*osh 2 c# dependency-injection solid-principles c#-8.0 default-interface-member

在 C#8 中,微软为接口方法引入了默认实现。这仍然是一个相当新的功能,似乎有很多关注的博主在写这个。

我想知道的是,默认实现是否有可能成为依赖倒置和 DI 的有用工具,或者它是否会促进不良的编程风格?它是否违反了任何众所周知的原则,如 SOLID?

Lua*_*aan 6

默认接口实现有两个主要设计目标。更重要的是回到有关设计界面的指南。特别是,一旦你发布了一个界面,它就应该是一成不变的,永远不会改变。问题是,这也是一条一直被忽视的规则……

第一个也是主要的实用程序是默认接口实现允许您将新成员引入接口,而不会破坏与该(公共)接口的使用者的源代码或二进制兼容性。这仍然限制了您在更改公共界面时可以进行的更改类型,但也使客户可以更轻松地使用新界面 - 升级是免费的,他们可以立即开始使用新功能。

第二个设计目标是用特征扩展类的方法——这在游戏开发中早已使用。基本思想是,您可以通过让类实现接口来向类添加新的明确定义的行为,同时还保留修改类本身行为的能力。这本质上是一种相对较弱的元编程形式。

当然,仅仅因为这些是设计目标并不意味着它们是您应该使用默认实现的唯一方式。但是如果你概括一下,你会得到这两个基本用途:

  • 扩展接口而不破坏兼容性并且不必版本接口。
  • 扩展类而不必创建从类继承中获得的刚性树层次结构。

事实上,您甚至可以争辩说,这比类继承更简单、更清晰、更强大。在某种程度上,这是从扩展方法开始的方法的延续——本质上,默认接口方法实现是一个扩展方法,它也是虚拟的。默认实现只能使用公共接口,但实现类也可以使用自己的隐藏状态。它为 C# 提供了一种有限形式的多重继承,而无需处理两个“父级”的状态如何连接在一起(因为接口没有任何状态)。

最后,如果您担心 SOLID 之类的原则,让我们试一试:

  • SRP - 没有真正的变化,在一个地方非破坏性地添加新逻辑的一些新选项。我说这是 SRP 的小胜利。
  • OCP - 您可以获得扩展类的强大新方法,而无需修改类本身,只要该类实现了一个新功能有意义的接口。但是您不能以这种方式更改现有类的行为,因此它仍然关闭以进行修改。在这里获胜非常有用。
  • LSP - 没有真正受到影响。允许您仅通过实现接口(可以选择根据需要覆盖该行为)向类添加新的可替代行为,从而取得了一些小胜利。
  • ISP - 这可以双向进行。如果您有合理的默认实现,默认实现可能会使拥有许多小接口更容易。但它们也可以鼓励您不断修改现有接口而不是添加新接口。
  • DIP - 基本不受影响。如果新抽象是对已有内容的合理扩展,或者可以在不依赖状态的情况下实现默认行为,则可以更轻松地添加新抽象。您也可以尝试使用默认行为作为契约,但在 IMO 中,与抽象方法(您还可以控制状态)相比,它仍然没有诱惑力。