如何在 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…

cmd看excel有多少个子表_Excel中多个工作簿(工作表)如何合并为1个工作簿(工作表)?...

看到Excel多表合并,其实常见的有2种场景场景一:多个Excel文件需要合并为1个Excel文件(多个工作簿合并为1个工作簿)场景二:1个Excel文件种有多个工作表,需要合并为1个工作表首先来看下,场景一(多个Excel文件合并为1个文…

如何在 ASP.Net Core 中使用 LoggerMessage

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

希望不要T我~~哈哈...

不在学校,上网不便,账号未登入时间期限若到~还望别T,呵呵....我喜欢这里~多谢老大!!!!技术成就梦想!!!!!转载于:https://blog.51cto.c…

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

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

7段均衡器最佳参数_介绍一下十段均衡器的设置和参数

介绍一下十段均衡器的设置和参数【第一章.EQ的基本定义】EQ是Equalizer的缩写,大陆称为均衡器,港台称为等化器。作用是调整各频段信号的增益值。10段均衡器表示有10个可调节节点。节点越多,便可以调节出更精确的曲线,同时难度更大…

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

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

circle后面是什么意思 python_Ape circle Python操作-第2-01章-列表操作,小猿圈,作业

# 写代码,有如下列表,按照要求实现每一个功能li [alex, eric, rain]# 计算列表长度并输出length len(li)print(f1-li的长度是:{length})# 列表中追加元素“seven”,并输出添加后的列表li.append(seven)print(2-追加元素后的li是…

python插入排序_python 插入排序,选择排序

插入排序: def insert_sort(lst): for i in range(1,len(lst)): xlst[i] #x是一个临时变量,表示当前轮到的数字 ji #临时变量 j ,为后续的排序提供方便 while j>0 and lst[j-1]>x: #当j 小于0,说明已经数字比较到了第一位,说…

【Vscode】调试DotNet Core代码

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

Visual Basic、C# 和 C++ 的数据类型比较(转)

类别类名说明Visual Basic 数据类型C# 数据类型C 托管扩展数据类型JScript 数据类型整数Byte8 位的无符号整数。BytebytecharByteDZX7¢&dot [url]www.cnntec.com[/url]…gT‚sSByte8 位的有符号整数。 不符合 CLS。DZX7¢&dot [url]www.cnntec.com[/url]…gT‚…

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

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

mysql 精度_mysql-笔记 精度

decimal数据类型是fixed-point类型,计算结算是准确的。numeric/dec/fixed / integerfloat/double数据类型是 floating-point类型,计算结果是大约的。float/double/double precision/realDecimal(M,D):M 最大精度位数 1到65D小数位数 0到30,不…

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…

libaio.so.1 mysql_libaio.so.1()(64bit) is needed by MySQL-server

从官网下的mysql5.5的server,系统是centos 6.4x86_64,安装时一直报错:[rootdbserver opt]# rpm -ivh MySQL-server-5.5.31-2.el6.x86_64.rpmerror: Failed dependencies:libaio.so.1()(64bit) is needed by MySQL-server-5.5.31-2.el6.x86_64libaio.so.1…

docker 安装kafka_laradock 中如何安装 Laravel Dusk

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

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

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