翻译自 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 { } // OK

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

CIA.M 的最终替代是 MIA 中声明的具体方法。

请注意,类只能实现接口,而不会从接口继承成员

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"
}

作者 : John Demetriou

译者 : 技术译民

出品 : 技术译站

链接 : 英文原文


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

  2. https://www.cnblogs.com/ittranslator/p/13838080.html 菱形问题

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

C# 8: 默认接口方法的更多相关文章

  1. Java8 新特性之默认接口方法

    摘要: 从java8开始,接口不只是一个只能声明方法的地方,我们还可以在声明方法时,给方法一个默认的实现,我们称之为默认接口方法,这样所有实现该接口的子类都可以持有该方法的默认实现. · 待定 一. ...

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

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

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

    30分钟入门Java8之默认方法和静态接口方法 前言 上一篇文章30分钟入门Java8之lambda表达式,我们学习了lambda表达式.现在继续Java8新语言特性的学习,今天,我们要学习的是默认方 ...

  4. Java8之默认方法和静态接口方法

    前言 上一篇文章30分钟入门Java8之lambda表达式,我们学习了lambda表达式.现在继续Java8新语言特性的学习,今天,我们要学习的是默认方法和静态接口方法. 这一Java8的新语言特性, ...

  5. 编写高质量代码改善C#程序的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]

    前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正. 建议1. ...

  6. 【Android 多媒体开发】 MediaPlayer 状态机 接口 方法 解析

    作者 : 韩曙亮 转载请著名出处 :  http://blog.csdn.net/shulianghan/article/details/38487967 一. MediaPlayer 状态机 介绍 ...

  7. 编写高质量代码改善C#程序的157个建议——建议2: 使用默认转型方法

    建议2: 使用默认转型方法 除了字符串操作外,程序员普遍会遇到的第二个问题是:如何正确地对类型实现转型.在上一个建议中,从int转型为string,我们使用了类型int的ToString方法.在大部分 ...

  8. C#8.0 中使用默认接口成员更新接口

    连载目录    [已更新最新开发文章,点击查看详细] 从 .NET Core 3.0 上的 C# 8.0 开始,可以在声明接口成员时定义实现. 最常见的方案是安全地将成员添加到已经由无数客户端发布并使 ...

  9. C#程序编写高质量代码改善的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]

    前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正. 建议1. ...

随机推荐

  1. 在windows下使用pip安装python包遇到缺失stdint.h文件的错误

    今天在windows上使用pip安装一个python包python-lzf时遇到如下的错误: fatal error C1083: Cannot open include file: 'stdint. ...

  2. apache环境搭建(xampp跑不起来)

    写在前面:之前(2016年)学jq时,学到ajax时,需要搭建apache,我开始安的xampp集成好的软件包,但是我的电脑咋弄都跑不起来,找了好多方法还是没用,最后无奈宣告放弃.但是皇天不负有心人, ...

  3. Python反转链表

    # -*- coding:utf-8 -*- # class ListNode: # def __init__(self, x): # self.val = x # self.next = None ...

  4. Arnold变换(猫脸变换)

    Arnold变换是Arnold在遍历理论研究中提出的一种变换.由于Arnold本人最初对一张猫的图片进行了此种变换,因此它又被称为猫脸变换.Arnold变换可以对图像进行置乱,使得原本有意义的图像变成 ...

  5. redis并发问题2

    转自https://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247485464&idx=1&sn=8d690fc6f878 ...

  6. spring framework源码之AnnotationConfigApplicationContext

    AnnotationConfigApplicationContext 内部使用了AnnotatedBeanDefinitionReader:ClassPathBeanDefinitionScanner ...

  7. 微服务实战系列(七)-网关springcloud gateway

    1. 场景描述 springcloud刚推出的时候用的是netflix全家桶,路由用的zuul,但是据说zull1.0在大数据量访问的时候存在较大性能问题,2.0就没集成到springcloud中了, ...

  8. Logback自定义日志颜色

    片段 1 片段 2 LogbackColorful.java package cn.mrxionge.netdemo; import ch.qos.logback.classic.Level; imp ...

  9. Docker系列——利用gogs搭建属于自己的git服务

    gogs简介 Gogs的目标是打造一个最简单.最快速和最轻松的方式搭建自助Git服务.使用Go语言开发使得Gogs能够通过独立的二进制分发,并且支持Go语言支持的所有平台,包括 Linux.Mac O ...

  10. Python练习题 018:打印星号菱形

    [Python练习题 018] 打印出如下图案(菱形): * *** ***** ******* ***** *** * --------------------------------------- ...