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

如何在 C# 8 中使用默认接口方法相关推荐

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

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

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

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

  3. C# 8: 默认接口方法

    翻译自 John Demetriou 2018年8月4日 的文章 <C# 8: Default Interface Methods>[1] C# 8 之前 今天我们来聊一聊默认接口方法.听 ...

  4. 如何在Spring boot中修改默认端口

    文章目录 介绍 使用Property文件 在程序中指定 使用命令行参数 值生效的顺序 如何在Spring boot中修改默认端口 介绍 Spring boot为应用程序提供了很多属性的默认值.但是有时 ...

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

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

  6. C# 8.0 中开启默认接口实现

    当你升级到 C# 8.0 和 .NET Core 3.0 之后,你就可以开始使用默认接口实现的功能了. 从现在开始,你可以在接口里面添加一些默认实现的成员,避免在接口中添加成员导致大量对此接口的实现崩 ...

  7. 计算机配置 凭据分配,如何在Windows 10中分配默认凭据提供程序 | MOS86

    点击此处修复Windows错误并提高系统性能 我们都知道Windows 10带有多个登录选项.这是可能的,因为在操作系统中存在各种凭证提供商.在Windows的早期版本Windows XP,Windo ...

  8. outlook默认签名设置_如何在Outlook 2013中分配默认签名

    outlook默认签名设置 If you sign most of your emails the same way, you can easily specify a default signatu ...

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

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

最新文章

  1. win7,windowsXP安装mysql-5.1.49-win32,中文版、英文版,通吃
  2. python arp扫描_基于python的局域网arp扫描
  3. 基础排序算法详解与优化
  4. 插件使用之加载自定义lua脚本
  5. 献给写作者的 Markdown 新手指南
  6. Cocos游戏引擎3D特效全新升级 更流畅更炫酷
  7. cmd,powershell,的字体颜色永久改变,;以及两者不同打开方式和其各自的区别。
  8. easyui 排序实现
  9. C#正则表达式替换字符串
  10. 结构型设计模式在公司项目中的运用实践
  11. datetimepicker 默认时间_Django项目中如何使用日期时间选择器DateTimePicker
  12. Single-page application
  13. XML的DTD约束和Schema约束
  14. python获取本机IP、探测指定URL的IP等信息、生成格式化时间
  15. Unity 之 发布 WebGl 遇到的问题
  16. python mro
  17. linux开启IP转发
  18. Re-ID with Triplet Loss
  19. 如何解决The emulator process for AVD was killed.
  20. echarts 中国地图 世界地图

热门文章

  1. Python_XlrdXlwt
  2. linux里查看最耗CPU的线程
  3. 数据基本类型以及相关举例
  4. [Programming WCF Services]Chapter 1. WCF Essentials - Metadata Exchange
  5. 关于android MTK相机L版本,切换屏幕比例后,分辨率随之改变,但重新进入相机后原有分辨率不再生效问题...
  6. 1.单机部署hadoop测试环境
  7. Technical User Stories – What, When, and How?
  8. AsyncHttpClient的连接池使用逻辑
  9. 淘宝一淘网收录部分垂直B2C网站信息
  10. 修正CS2.0中的日历头显示错误