时间:2021-05-19
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() 方法被最先调用。
到这里,我想你肯定有疑问,抽象类 和 接口 是不是很相似了,甚至可以互换了?虽然抽象类和接口现在看起来在很多方面都很相似,但两者之间还是有微妙的区别的,具体如下:
默认接口方法 允许开发人员利用 trait 编程技术,该技术可以让那些附属于该方法的不相关类型得以继续使用,可能你有点懵,我举个例子:假设你构建好了一个dll,被很多的开发人员所使用,现在你要发布该 dll 的新版本,比如说往接口中添加了新方法,这个时候你可以定义默认实现,这样就可以对已使用的开发者进行无感升级。
译文链接:https:///article/3455239/how-to-use-default-interface-methods-in-csharp-8.html
到此这篇关于在C# 8中如何使用默认接口方法的文章就介绍到这了,更多相关C#8使用默认接口方法内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本文实例讲述了C#接口在派生类和外部类中的调用方法。分享给大家供大家参考,具体如下:C#的接口通过interface关键字进行创建,在接口中可以包含属性,方法等
Android中Lambda表达式的使用实例详解Java8中着实引入了一些非常有特色的功能,如Lambda表达式、streamAPI、接口默认实现等等。Lamb
本文实例讲述了C#接口实现方法。分享给大家供大家参考。具体如下:在讲解C#实现接口的实例解析之前我们来看看C#接口的定义,如果一个类派生于一个接口,它就会执行某
详解C#编程获取资源文件中图片的方法本文主要介绍C#编程获取资源文件中图片的方法,涉及C#针对项目中资源文件操作的相关技巧,以供借鉴参考。具体内容如下:例子:u
本文实例分析了C#中使用资源的方法。分享给大家供大家参考。具体如下:这里总结一个在C#中如何使用资源的方法如下:方法一、使用本地文件1、将本地要加入的资源文本(