翻译自 John Demetriou 2018年8月4日 的文章 《C# 8: Default Interface Methods》[1]

C# 8 之前

今天我们来聊一聊默认接口方法。听起来真的很奇怪,不是吗?接口仅用于定义契约。接口的实现类会拥有一组公共方法,不过实现类被赋予了以其自己的方式实现每个方法的自由。目前为止,如果我们还需要为这些方法中的一个或多个方法提供实现,我们将使用继承。
如果我们希望这个类不是实现所有方法,而只是实现其中的一个子集,我们可以将这些方法和类本身抽象(abstract)。

例如,我们不能这么写:

interface IExample
{void Ex1();                                      // 允许void Ex2() => Console.WriteLine("IExample.Ex2"); // 不允许(C# 8 以前)
}

我们不得不用下面的抽象类来替代:

abstract class ExampleBase
{public abstract void Ex1();public void Ex2() => Console.WriteLine("ExampleBase.Ex2");
}

不过还好,这已经足够满足我们的大部分需求了。

C# 8 之后

那么,有什么改变吗?为什么我们需要引入这个新特性?我们错过了什么并且从未注意到我们错过了什么?

菱形问题

由于菱形问题[2],C#(以及许多其他语言)不支持多重继承。为了允许多重继承,同时避免菱形问题,C# 8 引入了默认接口方法。

从 C# 8 开始,使用默认接口方法,您可以拥有一个接口定义,以及该定义中某些或所有方法的默认实现。

interface IExample
{void Ex1();                                      // 允许void Ex2() => Console.WriteLine("IExample.Ex2"); // 允许
}

因此,现在您可以实现一个含有已实现方法的接口,并且可以避免希望从特定类(也包含通用方法)继承的类中的代码重复。

使用默认接口方法,菱形问题并没得到百分之百解决。当一个类继承自从第三个接口继承而来的两个接口,并且所有接口都实现了相同方法时,仍然可能发生这种情况。
在这种情况下,C# 编译器将根据当前上下文选择调用适当的方法。如果无法推断出特定的哪一个,则会显示编译错误。

例如,假设我们有以下接口:

interface IA
{void DoSomething();
}interface IB : IA
{void DoSomething() => Console.WriteLine("I am Interface B");
}interface IC : IA
{void DoSomething() => Console.WriteLine("I am Interface C");
}

然后,我创建一个实现上述两个接口的类 D,会引发一个编译错误:

//编译器提示:“D”未实现接口成员“IA.DoSomething()”
public class D : IB, IC
{ }

但是,如果类 D 实现它自己版本的 DoSomething 方法,那么编译器将知道调用哪个方法:

public class D : IB, IC
{public void DoSomething() => Console.WriteLine("I am Class D");
}

若 Main 方法代码如下:

static void Main()
{var x = new D();x.DoSomething();Console.ReadKey();
}

运行程序,控制台窗口输出:I am Class D

其他益处

使用方法的默认接口实现,API 提供者可以扩展现有接口而不破坏遗留代码的任何部分。

Trait 模式

译者注:
在计算机编程中,特征(Trait)是面向对象编程中使用的一个概念,它表示可用于扩展类的功能的一组方法。[3]

Trait 模式大体上就是多个类需要的一组方法。
在此之前,C# 中的 Trait 模式是使用抽象类实现的。但是由于多重继承不可用,实现 Trait 模式变得非常棘手,所以大多数人要么避开它,要么迷失在一个巨大的继承链中。

不过,在接口中使用默认方法实现,这将发生改变。我们可以通过在接口中使用默认接口方法实现,提供一组需要类拥有的方法,然后让这些类继承此接口。
当然,任何一个类都可以用它们自己的实现覆盖这些方法,但是以防它们不希望这么做,我们为它们提供了一组默认的实现。

以下为译者补充

接口中的具体方法

默认接口方法的最简单形式是在接口中声明具体方法,该方法是具有主体部分的方法。

interface IA
{void M() { Console.WriteLine("IA.M"); }
}

实现此接口的类不必实现其具体方法。

class C : IA { } // OKstatic void Main()
{IA i = new C();i.M(); // 输出 "IA.M"
}

类 C 中 IA.M 的最终替代是在 IA 中声明的具体方法 “M” 。
请注意,类只能实现接口,而不会从接口继承成员

C c = new C(); // 或者 var c = new C();
c.M();         // 错误: 类 'C' 不包含 'M' 的定义

但如果实现此接口的类也实现了具体方法,则同一般的接口含义是一样的:

class C : IA
{public void M() { Console.WriteLine("C.M"); }
}static void Main()
{IA i = new C();i.M(); // 输出 "C.M"
}

相关链接:

  1. https://www.devsanon.com/c/c-8-default-interface-methods/ C# 8: Default Interface Methods ↩︎

  2. https://mp.weixin.qq.com/s/EZ_jIjT6hYFrhbJ9BZ7Amw 菱形问题 ↩︎

  3. https://en.wikipedia.org/wiki/Trait_(computer_programming) Trait ↩︎

作者 :John Demetriou

译者 :技术译民 
出品 :技术译站(https://ITTranslator.cn/)

C# 8: 默认接口方法相关推荐

  1. C#和F#默认接口方法更新

    "默认接口方法(Default Interface Methods)"特性提案将允许C#.F#及其他.NET语言实现有限形式的多继承.受Java的默认方法启发,库作者将可以向已发布 ...

  2. C# 8中的默认接口方法

    \ 关键要点 \\ 默认接口方法已经被包含在C# 8的新功能建议中,开发人员可以像使用trait那样使用默认方法.\\t trait是面向对象的编程技术,用于提升不相关类之间方法的重用性.\\t C# ...

  3. 如何在 C# 8 中使用默认接口方法

    C# 8 中新增了一个非常有趣的特性,叫做 默认接口方法 (又称虚拟扩展方法),这篇文章将会讨论 C# 8 中的默认接口方法以及如何使用. 在 C# 8 之前,接口不能包含方法定义,只能在接口中定义方 ...

  4. C# 默认接口方法更新完成,很多细节问题尚待解决

    随着对默认接口方法的支持越来越接近完成,一些潜在的问题被提了出来.虽然已经完成了很多工作,但这是一个复杂的特性,许多细节问题还没有解决.但首先,这里有一些已解决的问题. 接口允许使用 static 和 ...

  5. C# 8.0 的默认接口方法

    例子 直接看例子 有这样一个接口: 然后有三个它的实现类: 然后在main方法里面调用: 截至目前,程序都可以成功的编译和运行. IPerson接口变更 突然,我想对所有的人类添加一个新的特性,例如, ...

  6. 30分钟入门Java8之默认方法和静态接口方法

    2019独角兽企业重金招聘Python工程师标准>>> 30分钟入门Java8之默认方法和静态接口方法 作者:@JohnTsai 本文为作者原创,转载请注明出处:http://www ...

  7. Go 学习笔记(35)— Go 接口 interface (接口声明、接口初始化、接口方法调用、接口运算、类型断言、类型查询、空接口)

    1. 接口概念 接口是双方约定的一种合作协议.接口实现者不需要关心接口会被怎样使用,调用者也不需要关心接口的实现细节.接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式.类型及结构. 接口内部存 ...

  8. php函数的默认值,php函数指定默认值方法的小例子

    php函数指定默认值方法的小例子 本节内容: php函数指定默认值 在php编程中,为自定义函数设定默认值,当用户调用该函数时,如果不给参数指定值,参数会用默认值顶替. 例1, 复制代码 代码如下: ...

  9. 【Kotlin】接口 ( 声明 | 实现 | 接口方法 | 接口属性 | 接口覆盖冲突 | 接口继承 )

    文章目录 I . 接口总结 II . 接口声明 III . 接口实现 IV . 接口中的方法 V . 接口中的属性 ( 变量 / 常量 ) VI . 接口中的属性属性覆盖 ( 变量 / 常量 ) VI ...

最新文章

  1. bzoj2961 共点圆 (CDQ分治, 凸包)
  2. 【IntelliJ IDEA】从资源文件读取出来就中文乱码的解决方法
  3. 如何删除未推送的git commit?
  4. 日本人真会玩!3天众筹60万元来造“机器猫”,会说话摇尾巴的那种
  5. 电脑安装python失败-解决“Windows 7 Python3.6 安装失败”问题
  6. myeclipse部署项目后,debug模式启动,总是弹出Class.class文件
  7. 标准C程序设计七---12
  8. java 大纲,Java学科学习大纲
  9. 《深入理解Kafka:核心设计与实践原理》笔误及改进记录
  10. python语言原理_梯度下降算法的原理用Python语言实现,易于理解,python,更
  11. 【算法分析与设计】基本算法设计方法的思想策略
  12. Bootstrap导航条中组件的排列
  13. flask v0.1 内部运行程序
  14. 20200712每日一句
  15. 什么是P = NP?问题
  16. 【老生谈算法】matlab实现细菌觅食算法(BFA)源码——粒子群算法
  17. css:overflow-x: overlay火狐浏览器不生效没有滚动条出现
  18. 设计师:设计师知识储备(设计分类、设计十种形式、设计要素、设计原则、室内设计风格流行趋势)之详细攻略
  19. SDCC 2016·北京站年终收官巨献,五十位演讲嘉宾和议题大公布
  20. (java)length与length() 的区别

热门文章

  1. Linux系统时间\硬件时间(date、tzselect、clock、hwclock、ntpdate)
  2. ReactNative--React简介
  3. ActiveMQ无法启动
  4. [异常解决] ubuntu上安采用sudo启动的firefox,ibus输入法失效问题解决
  5. Ajax:一种网页开发技术(Asynchronous Javascript + XML)
  6. 气溶胶光学厚度反演的两种方式(卫星探测和基地观测反演)
  7. mongo-rename操作
  8. 罗斯文2007(Northwind 2007)数据库、Access 2007 样列数据库分析[转]
  9. infoseccrypto_java下载_關於php接ICBC的支付接口的解決方案
  10. 迷茫在路口——致我的2014