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

C# 8 中新增了一个非常有趣的特性,叫做 默认接口方法 (又称虚拟扩展方法),这篇文章将会讨论 C# 8 中的默认接口方法以及如何使用。

在 C# 8 之前,接口不能包含方法定义,只能在接口中定义方法签名,还有一个就是接口的成员默认是 public 和 abstract , 在 C# 8 之前,接口不能包含字段,也不能包含private, protected, 或者 internal 的方法成员。如果你在接口中引入了一个新成员,默认情况下你必须更新实现该接口的所有子类。

在 C# 8 中可以在接口定义方法的默认实现,而且还可以定义接口成员为 private,protect,甚至是 static,还有一点挺奇葩的,一个接口的 protect 成员是不能被实现类所访问的,相反,它只能在子接口中被访问,接口的 virtual 成员可以由派生接口 override,但不能被派生类 override,还有一点请注意,接口目前还不能定义 实例成员

为什么要使用默认接口方法

所谓的 默认接口方法 指的是接口中定义了一个默认实现的方法, 如果实现该接口的类没有实现默认接口方法的话,那么这个 默认接口方法 只能从接口上进行访问,这是一个很有用的特性,因为它可以帮助开发人员在不破坏现有功能的情况下向接口的未来版本添加新方法。

考虑下面的 ILogger 定义。

public interface ILogger{public void Log(string message);}

下面的两个类扩展了ILogger接口并实现了Log()方法。

public class FileLogger : ILogger{public void Log(string message){//Some code}}public class DbLogger : ILogger{public void Log(string message){//Some code}}

现在假设你想在ILogger接口中新增一个方法,该方法接受两个参数:一个 文本 一个 日志级别,下面的代码片段展示了日志级别的枚举类。

public enum LogLevel{Info, Debug, Warning, Error}

修改后的 ILogger 接口如下:

public interface ILogger{public void Log(string message);public void Log(string message, LogLevel logLevel);}

好了,现在问题来了,因为 ILogger 中新增了一个 Log 方法,你必须要在所有实现该接口的所有子类中实现 Log(string message, LogLevel logLevel) 方法,这就很尴尬了,如果不这样做的话,编译器肯定是不会放行的,在现实情况下,这个接口实现类可能在多个 dll 中,甚至在多个团队中,可想而知,这个工作量是非常大并且非常痛苦的。

默认接口方法案例

这就是 默认接口方法 的应用场景,你可以在接口中定义一个默认方法是实现,如下代码所示:

public interface ILogger{public void Log(string message);public void Log(string message, LogLevel logLevel){Console.WriteLine("Log method of ILogger called.");Console.WriteLine("Log Level: "+ logLevel.ToString());Console.WriteLine(message);}}

这个时候,实现 ILogger 接口的子类可以不实现新的 Log(string message, LogLevel logLevel) 方法,因此下面的代码也是跑的通的,编译器不会抛出任何错误。

public class FileLogger : ILogger{public void Log(string message){//Some code}}public class DbLogger : ILogger{public void Log(string message){//Some code}}

默认接口方法不能被继承

现在创建一个 FileLogger 类实例,然后直接调用新的带参数的 Log() 方法,如下代码所示:


FileLogger fileLogger = new FileLogger();
fileLogger.Log("This is a test message.", LogLevel.Debug);

从上面图可看出 默认接口方法 不能被子类继承,换句话说,子类根本就不知道接口中还有带参数的 Log() 方法。

默认接口方法和菱形问题

现在有一个非常重要的问题,默认接口方法如何避免 菱形问题?换句话说就是 接口的 多继承 问题,考虑下面的代码清单。

public interface A{public void Display();}public interface B : A{public void Display(){Console.WriteLine("Interface B.");}}public interface C : A{public void Display(){Console.WriteLine("Interface C.");}}public class MyClass : B, C{}

当编译上面代码时,会抛出一个编译错误,说 MyClass 没有实现 A.Display() 方法,解决这个问题很简单,在 MyClass 中实现一下接口方法就可以了,如下代码所示:

public interface A{public void Display();}public interface B : A{public void Display(){Console.WriteLine("Interface B.");}}public interface C : A{public void Display(){Console.WriteLine("Interface C.");}}public class MyClass : B, C{public void Display(){Console.WriteLine("MyClass.");}}

接下来就可以生成 MyClass 实例了,然后再调用 Display() 方法,如下代码所示:

static void Main(string[] args){A obj = new MyClass();obj.Display();Console.Read();}

现在问题来了,到底是哪一个 Display() 方法被调用了呢?为了避免歧义,C# 将会使用最近覆盖规则,即 Class.Display() 方法被最先调用。

抽象类 VS 接口

到这里,我想你肯定有疑问,抽象类接口 是不是很相似了,甚至可以互换了?虽然抽象类和接口现在看起来在很多方面都很相似,但两者之间还是有微妙的区别的,具体如下:

  • 抽象类可以有实例成员,接口则不能。

  • 抽象类不能多继承,接口还是可以的。

默认接口方法 允许开发人员利用 trait 编程技术,该技术可以让那些附属于该方法的不相关类型得以继续使用,可能你有点懵,我举个例子:假设你构建好了一个dll,被很多的开发人员所使用,现在你要发布该 dll 的新版本,比如说往接口中添加了新方法,这个时候你可以定义默认实现,这样就可以对已使用的开发者进行无感升级。

译文链接:https://www.infoworld.com/article/3455239/how-to-use-default-interface-methods-in-csharp-8.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/305407.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.Net下二进制形式的文件(图片)的存储与读取 [ZT]

.Net下图片的常见存储与读取凡是有以下几种:存储图片:以二进制的形式存储图片时,要把数据库中的字段设置为Image数据类型(SQL Server),存储的数据是Byte[].1.参数是图片路径:返回Byte[]类型: publicbyte[] GetPictureData(stringimagepath) { /**…

c++tcp接收文件缓存多大合适_网易面经:深剖TCP协议的流量控制和拥塞控制,你懂了吗?...

1.自我介绍项目2.RPC框架和普通http有什么区别和优势? 基于Tcp封装还是http封装的3.rpc是长连接吗?如果要传输一个特别大的文件 底层还是基于流吗? Nio是一个什么IO模型?4.github了的watch star fork5.异常和error的区别&#xff…

如何在 ASP.Net Core 中使用 LoggerMessage

ASP.NET Core 是一个开源的、跨平台的、轻量级模块化框架,可用于构建高性能、可伸缩的web应用程序,你也许不知道 ASP.NET Core 中有一个藏得很深,而且非常强大的特性,那就是 LoggerMessage,与内建的 Logger 相比&#…

模拟器显示空白图片_Kawaks街机模拟器,还是小时候的味道!

APP菜园打造属于我们自己的APP帝国1.打开支付宝app,首页搜索 “8221050” 然后点击快捷功能,天天领红包。2.软件领取流程:1.认真阅读下软件介绍;2.找到图片下方的“获取链接”复制;3.打开手机上的浏览器粘贴链接搜索(…

ASP.NET Core 查看应用状态和统计

在日常开发中,我们需要关注 .NET 应用的资源使用情况,方便排查问题和扩容。通过 Ajax 请求获取统计信息,展示成图表,如下图:CLRStats 插件,一个统计 .NET 应用资源使用情况的插件,包含&#xff…

【Vscode】调试DotNet Core代码

Visual Studio作为宇宙第一的IDE,开发调试.net core app,无一能出其右,我们还需要去了解Visual Studio Code吗?答案是肯定。杀鸡焉用牛刀:就一个hello world的Console App,还需要打开Visual Studio吗&#…

pandownload 卢本伟_PanDownload复活了!60MB/s!附下载地址

最近几天,听说PanDownload 复活了有人接盘了,重新制作上线推出了更加强劲的复活版!但是笔者去下载了一下,发现并不能使用于是经过百般搜寻,发现被人提供的已经是旧版了于是,我找到了最新版,9月2…

linux安装rz命令_Linux 安装dep安装包命令

rootlsy-ubuntu:~# dpkg -i sublime-text_build-3126_amd64.deb 正在选中未选择的软件包 sublime-text。(正在读取数据库 ... 系统当前共安装有 256092 个文件和目录。)正准备解包 sublime-text_build-3126_amd64.deb ...正在解包 sublime-text (3126) ...正在设置 sublime-tex…

Excel分列功能的妙用

我们用left函数可以将姓名中的姓和名分开了,说实话,这个方法比较笨,因为EXCEL中提供了分列功能。 我们可以看下面这个例子,原始数据中含有编号,而且随着行数的增加,编号位数也在增加,这时就不能…

IdentityServer4之Implicit和纯前端好像很配哦

前言上一篇Resource Owner Password Credentials模式虽然有用户参与,但对于非信任的第三方的来说,使用这种模式是有风险的,所以相对用的不多;这里接着说说implicit隐式模式,这种模式比较适合于纯前端客户端&#xff0c…

docker 安装kafka_laradock 中如何安装 Laravel Dusk

laradock 中如何安装 Laravel Dusk?导语在本地安装 Laravel Dusk 一直失败,查了文档才发现在 laradock 中并不是只需要 composer require 就可以的,还有其他配置。下面记录一下。配置 laradock1.切换到 laradock 目录中,将 worksp…

.NET 中依赖注入组件 Autofac 的性能漫聊

Autofac 是一款超赞的 .NET IoC 容器 ,在众多性能测评中,它也是表现最优秀的一个。它管理类之间的依赖关系, 从而使 应用在规模及复杂性增长的情况下依然可以轻易地修改。它的实现方式是将常规的.net类当做 组件 处理。简单的性能测试在 LINQPad 中&…

c语言 extern_Visual C++ 6.0环境中C语言extern 变量使用过程报错

extern 变量称为外部存储变量。extern声明了程序中将要用到但尚未定义的外部变量。本次错误的解决方法同样适用于外部函数。外部函数就是可以被其他源文件调用的函数,定义外部函数时使用extern进行修饰。首先,分别创建两个源文件“ex1.c”和“ex2.c”。其…

盘点大厂的那些开源项目 - 华为

HarmonyOS鸿蒙系统(HarmonyOS),是第一款基于微内核的全场景分布式OS,是华为自主研发的操作系统。华为会率先部署在智慧屏、车载终端、穿戴等智能终端上,未来会有越来越多的智能设备使用开源的鸿蒙OS。鸿蒙 OS 底层由鸿…

ubuntu mysql怎么备份_Ubuntu下MySQL备份与异地备份

下面是一则MySQL备份实例,在Ubuntu下测试通过。完成任务是本地备份。可用于服务器端。#! /bin/bashecho "backupmysql V1.0"###date stamp###datestamp$(date %Y-%m-%d)###path ###startdir/home/user/mysqlbackup###bakfile prefix###fileprefixsqlecho …

java 位运算_java学习之运算符与表达式(四)

(6)位运算符位运算是指对整数按二进制的位进行运算。位运算用于整数或字符类型。有7个&#xff1a;~(非)、&(与)、&#xff5c;(或)、^(异或)、<>(右移)、>>>(无符号右移)。例&#xff1a;以byte类型4为例。byte类型占用1个字节。4的二进制为&#xff1a;00…

【Http】一文备忘Http状态码(406,415,422)

最近在调试接口时&#xff0c;web api 报了一个415状态码。好久没见到这个状态码&#xff0c;一时还真不知道啥情况。所以&#xff0c;人的大脑是有遗忘规律的&#xff0c;为了加深印象&#xff0c;所以我觉得我有必要再复习一下。1.HTTP的状态码 首先复习一下所有的状态码。1x…

infor wms 项目启动_广汽本田增城工厂No.2L WMS项目正式启动

Warehouse导读&#xff1a;近日&#xff0c;在生管科No.2L办公室&#xff0c;广汽本田增城工厂No.2L WMS项目启动大会如期召开。生产管理科&#xff0c;IT科&#xff0c;FNL以及汉得信息有限公司的WMS项目组成员参加了本次启动大会。Management2018年10月&#xff0c;生管科已经…

mysql动态扩展_动态可扩展查询MYSQL5.7JSON+虚拟列+Mybatis

背景&#xff1a;现有业务扩展字段&#xff0c;都存在feature字段&#xff0c;存在语义不清晰以及&#xff0c;难以利用索引查询问题Mysql 5.7后推出利器&#xff0c;JSON虚拟列&#xff0c;即实现了业务语义统一&#xff0c;也支持索引查询加速一、简单描述MySQL 5.7.7 labs版…

asp.net core安全事项(中)

上传文件上传文件是造成风险的很大因素&#xff0c;所以对上传文件进行处理是重要的&#xff0c;首先要处理的是&#xff1a;a、上传文件大小限制&#xff1b;b、上传文件类型(能过扩展或&#xff0c;或文件头)限制&#xff1b;c、上传的名称要替换掉&#xff1b;d、上传的文件…