C# 8: 默认接口方法
翻译自 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"
}
类 C 中 IA.M 的最终替代是 M 在 IA 中声明的具体方法。
请注意,类只能实现接口,而不会从接口继承成员:
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
译者 : 技术译民
出品 : 技术译站
链接 : 英文原文
https://www.devsanon.com/c/c-8-default-interface-methods/ C# 8: Default Interface Methods ︎
https://en.wikipedia.org/wiki/Trait_(computer_programming) Trait ︎
C# 8: 默认接口方法的更多相关文章
- Java8 新特性之默认接口方法
摘要: 从java8开始,接口不只是一个只能声明方法的地方,我们还可以在声明方法时,给方法一个默认的实现,我们称之为默认接口方法,这样所有实现该接口的子类都可以持有该方法的默认实现. · 待定 一. ...
- C# 8.0 的默认接口方法
例子 直接看例子 有这样一个接口: 然后有三个它的实现类: 然后在main方法里面调用: 截至目前,程序都可以成功的编译和运行. IPerson接口变更 突然,我想对所有的人类添加一个新的特性,例如, ...
- 30分钟入门Java8之默认方法和静态接口方法
30分钟入门Java8之默认方法和静态接口方法 前言 上一篇文章30分钟入门Java8之lambda表达式,我们学习了lambda表达式.现在继续Java8新语言特性的学习,今天,我们要学习的是默认方 ...
- Java8之默认方法和静态接口方法
前言 上一篇文章30分钟入门Java8之lambda表达式,我们学习了lambda表达式.现在继续Java8新语言特性的学习,今天,我们要学习的是默认方法和静态接口方法. 这一Java8的新语言特性, ...
- 编写高质量代码改善C#程序的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]
前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正. 建议1. ...
- 【Android 多媒体开发】 MediaPlayer 状态机 接口 方法 解析
作者 : 韩曙亮 转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/38487967 一. MediaPlayer 状态机 介绍 ...
- 编写高质量代码改善C#程序的157个建议——建议2: 使用默认转型方法
建议2: 使用默认转型方法 除了字符串操作外,程序员普遍会遇到的第二个问题是:如何正确地对类型实现转型.在上一个建议中,从int转型为string,我们使用了类型int的ToString方法.在大部分 ...
- C#8.0 中使用默认接口成员更新接口
连载目录 [已更新最新开发文章,点击查看详细] 从 .NET Core 3.0 上的 C# 8.0 开始,可以在声明接口成员时定义实现. 最常见的方案是安全地将成员添加到已经由无数客户端发布并使 ...
- C#程序编写高质量代码改善的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]
前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正. 建议1. ...
随机推荐
- 吴恩达《深度学习》-课后测验-第一门课 (Neural Networks and Deep Learning)-Week 2 - Neural Network Basics(第二周测验 - 神经网络基础)
Week 2 Quiz - Neural Network Basics(第二周测验 - 神经网络基础) 1. What does a neuron compute?(神经元节点计算什么?) [ ] A ...
- Swift入门
Swift 入门 简介 Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序 2014 年,在 Apple WWDC 发布 历史 2010 年 7 月,苹果开发者工 ...
- thinkPHP5.1 MVC架构使用方法
MVC架构 1.M层:model(模型),是增强版的数据库 M层是用来存放自动完成代码.修改器(数据修改).模型事件.验证器 2.V层:view 显示视图 V层用来存放HTML.css.JavaScr ...
- ASP.NET Core 3.x启动时运行异步任务(二)
这一篇是接着前一篇在写的.如果没有看过前一篇文章,建议先去看一下前一篇,这儿是传送门 一.前言 前一篇文章,我们从应用启动时异步运行任务开始,说到了必要性,也说到了几种解决方法,及各自的优缺点.最 ...
- kubernetes的思考
初识k8s kubernetes,从接触到今年6月接触到现在有3个月了,严格来说是断断续续的接触,没有一直持续学习.在未接触之前,这个技术对我来说,有点像传说,运维同行对此评价普遍是比较难懂,概念庞大 ...
- 微服务实战系列(二)-注册中心Springcloud Eureka客户端
1. 场景描述 前几天介绍了下springcloud的Eureka注册中心(springcloud-注册中心快速构建),今天结合springboot-web介绍下eureka客户端服务注册. 2. 解 ...
- http走私攻击
http走私攻击 前端服务器和后端服务器接收数据不同步,引起对客户端传入的数据理解不一致,从而导致漏洞. 因为当服务器同时在处理Content-Length和Transfer-Encoding,Con ...
- 手把手教你基于CentOS8搭建微信订阅号后台服务(一)
一.准备域名并完成解析 关于域名,我买的是阿里的一个1元/年的廉价域名,同时国内域名都需要备案,当时在这里耽搁了挺久的. 域名解析的话,在阿里云官方帮助文档里有.传送门:https://help.al ...
- Git的使用--码云
Git的使用--码云 进入码云官网:https://gitee.com/ 注册or登录账号进入gitee页面(页面结构大同小异). 点击右上角加号--新建仓库,用于存放项目代码 创建项目需要注意的选项 ...
- Tomcat 第四篇:请求处理流程(上)
1. 引言 既然是在讲 Tomcat ,那么一个 HTTP 请求的请求流程是无论如何也绕不开的. 首先抛开所有,使用我们现有的知识面,猜测一下一个请求被 Tomcat 处理的过程: 1. 客户端(浏览 ...