本文转载请联系码农阅读公众号。C#8添加了一个非常有趣的特性,称为默认接口方法(也称为虚拟扩展方法),本文将讨论C#8中的默认接口方法以及如何使用它。在C#8之前,接口不能包含方法定义,只能在接口中定义方法签名。另一件事是接口的成员默认是公共和抽象的。在C#8之前,接口不能包含字段,也不能包含私有、受保护或内部方法成员。如果在接口中引入新成员,默认情况下必须更新实现该接口的所有子类。在C#8中,可以在接口中定义方法的默认实现,也可以将接口成员定义为private、protect,甚至是static。还有一点奇怪,接口的protect成员不能被实现类访问,相反,只能在子接口访问,接口的虚成员可以被派生接口覆盖,但不能被派生类覆盖,请注意接口还不能定义实例成员。为什么要使用默认接口方法所谓默认接口方法,是指在接口中定义了一个默认实现的方法。如果实现接口的类没有实现默认接口方法,那么只能从接口访问默认接口方法。这是一项有用的功能,因为它可以帮助开发人员在不破坏现有功能的情况下向未来版本的界面添加新方法。考虑下面的ILogger定义。publicinterfaceILogger{publicvoidLog(stringmessage);}以下两个类扩展了ILogger接口并实现了Log()方法。publicclassFileLogger:ILogger{publicvoidLog(stringmessage){//Somecode}}publicclassDbLogger:ILogger{publicvoidLog(stringmessage){//Somecode}}现在假设你想在ILogger接口中添加一个新方法,它接受两个参数:一个文本一个日志级别,下面的代码片段展示了日志级别的枚举类。publicenumLogLevel{Info,Debug,Warning,Error}修改后的ILogger接口如下:publicinterfaceILogger{publicvoidLog(stringmessage);publicvoidLog(stringmessage,LogLevellogLevel);}那么现在问题来了,因为ILogger新增了一个Log方法,你必须在所有实现这个接口的子类中实现Log(stringmessage,LogLevellogLevel)方法,这很尴尬。如果你不这样做,编译器肯定不会放过它。在现实中,这个接口实现类可能在多个dll中,甚至在多个team中。可想而知,这份工作量是非常大的,也是非常痛苦的。默认接口方法案例这是默认接口方法的应用场景。可以在接口中定义一个默认的方法来实现,如下代码所示::"+logLevel.ToString());Console.WriteLine(message);}}这时候实现了ILogger接口的子类可能没有实现newLog(stringmessage,LogLevellogLevel)方法,所以下面的代码也会工作,编译器不会抛出任何错误。publicclassFileLogger:ILogger{publicvoidLog(stringmessage){//Somecode}}publicclassDbLogger:ILogger{publicvoidLog(stringmessage){//Somecode}}默认接口方法不能继承现在创建一个FileLogger类实例,然后直接调用newLog带参数()方法,如下代码所示:FileLoggerfileLogger=newFileLogger();fileLogger.Log("Thisatestmessage.",LogLevel.Debug);从上图可以看出默认的接口方法是不能被子类继承的。不知道接口里面有带参数的Log()方法。默认接口方法和菱形问题现在有一个很重要的问题,默认接口方法如何避免菱形问题?换句话说,接口的多重继承问题,考虑下面的代码清单。publicinterfaceA{publicvoidDisplay();}publicinterfaceB:A{publicvoidDisplay(){Console.WriteLine("InterfaceB.");}}publicinterfaceC:A{publicvoidDisplay(){Console.WriteLine("InterfaceC.");}}publicclassMyClass:B,C{}上面的代码在编译的时候会抛出一个编译错误,说MyClass没有实现A.Display()方法。解决这个问题很简单。只需实现MyClass中的接口方法即可,如下代码所示:{Console.WriteLine("InterfaceC.");}}publicclassMyClass:B,C{publicvoidDisplay(){Console.WriteLine("MyClass.");}}接下来,可以生成MyClass实例,然后是Display()方法可以被调用,如下代码所示:叫做?为了避免歧义,C#会使用最近的重写规则,即Class的.Display()方法首先被调用。抽象类VS接口说到这里,我想你一定有疑问了。抽象类和接口是否非常相似,甚至可以互换?虽然现在抽象类和接口在很多方面看起来很相似,但是两者之间的区别还是有细微的差别,如下:抽象类可以有实例成员,但是接口不能。抽象类不能继承多个,但接口仍然可以。默认接口方法允许开发人员利用特征编程技术,允许附加到方法的无关类型继续使用。你可能有点困惑。举个例子:假设你构建了一个多人开发的dll,现在你想发布一个新版本的dll,比如你给接口添加了一个新的方法,你可以定义一个默认的实现在这一次,让您可以升级已经使用它的开发人员。翻译链接:https://www.infoworld.com/article/3455239/how-to-use-default-interface-methods-in-csharp-8.html
