【问题标题】:C# equivalent of Java 8 Default Methods in InterfaceC# 等效于接口中的 Java 8 默认方法
【发布时间】:2016-12-19 15:39:39
【问题描述】:

我听说在 Java 8 中,可以灵活地在接口中定义函数。我认为我们可以在所有实现此类接口的类中使用此功能具有一些默认状态。

那么,我的问题是,到目前为止,我们在 C# 中是否有任何此类功能?微软在这方面有什么计划吗?

【问题讨论】:

  • 你的意思是像一个基类吗?
  • @TheLethalCoder 从语义上讲,将它们放在基类中并不是一回事。
  • 您今天通过使用基类(可能是abstract 基类)来实现它,但是没有直接模拟 Java 8 支持的默认接口方法。 CLR/IL 在技术上允许实现此功能,但我认为未来的 C# 版本不会认真考虑它。
  • 它们不存在,我不相信有任何计划让它们存在 - 如果打算避免这些事情的适当解决方案通常是为新功能声明一个新接口中断 API 更改。这个想法从来不适合我——接口用于指定合同,而不是行为。
  • 将基础功能分解为实现接口的基类

标签: java c# default-interface-member


【解决方案1】:

更新

默认接口方法are a planned feature for C# 8

原答案

C# 没有这个确切的功能,但 Extension Methods 解决了与 Java 中引入的默认方法相同的问题。

为了将类似 LINQ 的函数方法引入 Java 8 中的常见集合,语言设计者想要一种方法来向接口添加方法,例如 Iterable<>。毕竟,.filter(a -> ...).map(a -> ...) 语法比 map(filter(a ->...), a2 -> ...) 更容易阅读,如果他们只是添加实用程序方法就必须这样做。然而,仅仅向接口添加方法签名将是一个重大更改,因为任何曾经实现该接口的人都会突然拥有在 Java 8 中构建的代码,除非他们实现了新方法。因此,他们开发了默认实现方法,以便在现有接口上添加新方法不会破坏现有代码。

几年前,C# 通过引入扩展方法解决了同样的问题。扩展方法并没有真正改变接口本身,而是让使用在实用程序类中定义的方法(如 .Where().Select() 方法)变得容易,就好像它实际上在目标对象上一样.

对扩展方法和默认实现的约束使它们在范围上非常相似。每个都有一些优点和缺点,我不会在这里讨论,但本质上它们只是解决同一问题的两种不同方法。

由于它与您的具体问题有关:扩展方法的缺点之一是(静态)它们破坏了面向对象代码的一些最佳实践:它们之间可能存在命名冲突,您可以例如,不要可靠地覆盖它们。因此,通常最好避免使用它们,除非您遇到无法通过任何其他方式轻松解决的问题。如果您只是希望提供方法的默认实现,通常最好使用基类,并期望人们扩展您的基类。

我想你会发现大多数 Java 专家都会对 Java 中的默认实现说同样的话。在 Java 8 之前它们没有被引入,因为普遍的观点是接口是用来定义什么事物能够做什么,而类是用来定义 如何事情已经完成。当然,你总能找到几个smart people who think there's no good reason to have interfaces in the first place。但是,如果您使用接口,大概是因为您看到了在不提供实现细节的情况下定义合约的价值。引入默认实现是为了解决一个非常具体的向后兼容性问题,如果你能从一开始就避免这个问题,那么我看不出有什么好的理由使用它们。

扩展方法同时更危险也更强大,所以除了向后兼容问题之外,它们还有一些很好的用途,但它们仍应谨慎使用,并且只有在其他更面向对象的方法胜出时才使用没用。

【讨论】:

  • 能否请您详细说明扩展方法如何解决此问题。您是否认为在这些接口之上使用扩展方法来实现默认功能,以便您以后可以在需要时使用它??请提供一些示例/场景来实现您在这方面的想法。
  • 事实上,扩展是更好、更灵活的解决方案
  • @JaganMohanReddy,您是否阅读了 StriplingWarrior 发布的链接?它很好地解释了您的情况。有一个接口和扩展方法的例子,你可以在其中实现相同的,你可以在java中的默认方法中实现
  • @JaganMohanReddy:我正在努力用更好的解释来更新我的帖子。请过几分钟再回来查看。
  • @AntP:是的,但是他们需要使静态包装器和实用方法更好地阅读的原因是使 LINQ 成为可能。 There's a reason 那个extension methods were introduced in the same version as lambda syntax。这与在 Java 中引入默认实现的原因与 lambda 语法相同。
【解决方案2】:

C# 中默认接口实现的官方语言提案记录在此:

https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md

目前标记为“提案”状态。

话虽如此,它有许多非常强大的用例。目前,正如 Mass Torgerson 和 Dustin Campbell 在下面的视频中所表明的那样,它似乎很有可能获得批准。如果是这样,几乎可以肯定它与 C#8 一起发布。

https://channel9.msdn.com/Events/Build/2017/B8104

53:00 左右,讨论开始并展示了演示。

【讨论】:

    【解决方案3】:

    C# 8.0 将引入默认接口实现的新特性。当添加新方法以保持与接口的现有实现的兼容性时,这很有用。它还可以提供简单的默认实现,如下例所示:

    public interface ILogger  
    {
        void Log(LogLevel level, string message);
    
        void Log(LogLevel level, string format, params object[] arguments)
        {
            Log(level, string.Format(format, arguments));
        }
    }
    

    New C# featues

    【讨论】:

    • 看起来不错,其实我不明白为什么 Java 会让你写那个烦人的“默认”关键字,而实际上它应该是多余的。
    【解决方案4】:

    我同意迄今为止在 cmets 中所做的陈述 - C# 不支持这一点。据我所知,没有计划在 C# 中支持这一点,也不应该支持它。我不同意 Java 8 也包含这个特性。我认为它将接口和抽象基类混为一谈。正如@AntP 在 cmets 中所说,接口应该是契约(而不是指定行为)。

    这里有两种可能的设计来完成同样的事情(对于仓促绘制的 UML 感到抱歉):

    基本上,要点是您可以创建一个抽象基类,为所有子类添加默认实现,或者您可以为某些实现接口的子类创建一个基类,如果这是你想做什么。

    显然其他设计也是可能的,此图主要用于说明目的。

    【讨论】:

    • 不完全一样。接口支持多重继承,类不支持。您可以在类中实现多个接口,而其中一些接口具有默认方法。抽象类不允许您完成此操作
    • @DenisItskovich 不,这不完全一样,但我认为我的基本观点没有改变。事实上,如果有的话,我认为这使得在接口中使用默认实现是一个更糟糕的主意,因为钻石问题。
    【解决方案5】:

    C# 不支持这一点,但我不同意 cmets 所说的接口应该是契约而不是实现。因为默认实现不是为了实现的麻袋,它是为了让客户端免于实现新更新的接口,如果他们不使用他们以前使用的新版本 api 中引入的新方法,就不让他们的旧冷中断.

    java 8 实现中的默认方法确实有助于解决更新 api 后出现的实际问题。

    【讨论】:

    • 这里的每个人都了解默认方法的用途,关键是它们在保持向后兼容性和避免破坏 API 更改方面是一种糟糕的方式。为新功能声明新接口要好得多。如果您确实需要默认实现,那么这很好地表明您一开始就错误地解决了问题。
    • 我觉得@mohammad-abu-hussen 提出了一个有用的观点,但对它被否决感到失望。我不同意每个人都明白他们的目的。不止一个动机通过促进语言的发展或提高与其他主要语言和平台的兼容性来间接影响大多数开发人员。我认为这样的帖子可以帮助人们以实用的方式理解高级主题背后的背景和推理,尤其是当他们提供相关的历史说明时。
    猜你喜欢
    • 2015-03-17
    • 1970-01-01
    • 2018-01-28
    • 1970-01-01
    • 2014-06-06
    • 1970-01-01
    • 1970-01-01
    • 2019-11-14
    • 1970-01-01
    相关资源
    最近更新 更多