在C# 8中如何使用默認(rèn)接口方法詳解
前言
C# 8 中新增了一個(gè)非常有趣的特性,叫做 默認(rèn)接口方法 (又稱(chēng)虛擬擴(kuò)展方法),這篇文章將會(huì)討論 C# 8 中的默認(rèn)接口方法以及如何使用。
在 C# 8 之前,接口不能包含方法定義,只能在接口中定義方法簽名,還有一個(gè)就是接口的成員默認(rèn)是 public 和 abstract , 在 C# 8 之前,接口不能包含字段,也不能包含private, protected, 或者 internal 的方法成員。如果你在接口中引入了一個(gè)新成員,默認(rèn)情況下你必須更新實(shí)現(xiàn)該接口的所有子類(lèi)。
在 C# 8 中可以在接口定義方法的默認(rèn)實(shí)現(xiàn),而且還可以定義接口成員為 private,protect,甚至是 static,還有一點(diǎn)挺奇葩的,一個(gè)接口的 protect 成員是不能被實(shí)現(xiàn)類(lèi)所訪問(wèn)的,相反,它只能在子接口中被訪問(wèn),接口的 virtual 成員可以由派生接口 override,但不能被派生類(lèi) override,還有一點(diǎn)請(qǐng)注意,接口目前還不能定義 實(shí)例成員。
為什么要使用默認(rèn)接口方法
所謂的 默認(rèn)接口方法 指的是接口中定義了一個(gè)默認(rèn)實(shí)現(xiàn)的方法, 如果實(shí)現(xiàn)該接口的類(lèi)沒(méi)有實(shí)現(xiàn)默認(rèn)接口方法的話,那么這個(gè) 默認(rèn)接口方法 只能從接口上進(jìn)行訪問(wèn),這是一個(gè)很有用的特性,因?yàn)樗梢詭椭_(kāi)發(fā)人員在不破壞現(xiàn)有功能的情況下向接口的未來(lái)版本添加新方法。
考慮下面的 ILogger 定義。
public interface ILogger { public void Log(string message); }
下面的兩個(gè)類(lèi)擴(kuò)展了ILogger接口并實(shí)現(xiàn)了Log()方法。
public class FileLogger : ILogger { public void Log(string message) { //Some code } } public class DbLogger : ILogger { public void Log(string message) { //Some code } }
現(xiàn)在假設(shè)你想在ILogger接口中新增一個(gè)方法,該方法接受兩個(gè)參數(shù):一個(gè) 文本 一個(gè) 日志級(jí)別,下面的代碼片段展示了日志級(jí)別的枚舉類(lèi)。
public enum LogLevel { Info, Debug, Warning, Error }
修改后的 ILogger 接口如下:
public interface ILogger { public void Log(string message); public void Log(string message, LogLevel logLevel); }
好了,現(xiàn)在問(wèn)題來(lái)了,因?yàn)?ILogger 中新增了一個(gè) Log 方法,你必須要在所有實(shí)現(xiàn)該接口的所有子類(lèi)中實(shí)現(xiàn) Log(string message, LogLevel logLevel) 方法,這就很尷尬了,如果不這樣做的話,編譯器肯定是不會(huì)放行的,在現(xiàn)實(shí)情況下,這個(gè)接口實(shí)現(xiàn)類(lèi)可能在多個(gè) dll 中,甚至在多個(gè)團(tuán)隊(duì)中,可想而知,這個(gè)工作量是非常大并且非常痛苦的。
默認(rèn)接口方法案例
這就是 默認(rèn)接口方法 的應(yīng)用場(chǎng)景,你可以在接口中定義一個(gè)默認(rèn)方法是實(shí)現(xiàn),如下代碼所示:
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); } }
這個(gè)時(shí)候,實(shí)現(xiàn) ILogger 接口的子類(lèi)可以不實(shí)現(xiàn)新的 Log(string message, LogLevel logLevel) 方法,因此下面的代碼也是跑的通的,編譯器不會(huì)拋出任何錯(cuò)誤。
public class FileLogger : ILogger { public void Log(string message) { //Some code } } public class DbLogger : ILogger { public void Log(string message) { //Some code } }
默認(rèn)接口方法不能被繼承
現(xiàn)在創(chuàng)建一個(gè) FileLogger 類(lèi)實(shí)例,然后直接調(diào)用新的帶參數(shù)的 Log() 方法,如下代碼所示:
FileLogger fileLogger = new FileLogger(); fileLogger.Log("This is a test message.", LogLevel.Debug);
從上面圖可看出 默認(rèn)接口方法 不能被子類(lèi)繼承,換句話說(shuō),子類(lèi)根本就不知道接口中還有帶參數(shù)的 Log() 方法。
默認(rèn)接口方法和菱形問(wèn)題
現(xiàn)在有一個(gè)非常重要的問(wèn)題,默認(rèn)接口方法如何避免 菱形問(wèn)題?換句話說(shuō)就是 接口的 多繼承 問(wèn)題,考慮下面的代碼清單。
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 { }
當(dāng)編譯上面代碼時(shí),會(huì)拋出一個(gè)編譯錯(cuò)誤,說(shuō) MyClass 沒(méi)有實(shí)現(xiàn) A.Display() 方法,解決這個(gè)問(wèn)題很簡(jiǎn)單,在 MyClass 中實(shí)現(xiàn)一下接口方法就可以了,如下代碼所示:
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."); } }
接下來(lái)就可以生成 MyClass 實(shí)例了,然后再調(diào)用 Display() 方法,如下代碼所示:
static void Main(string[] args) { A obj = new MyClass(); obj.Display(); Console.Read(); }
現(xiàn)在問(wèn)題來(lái)了,到底是哪一個(gè) Display() 方法被調(diào)用了呢?為了避免歧義,C# 將會(huì)使用最近覆蓋規(guī)則,即 Class.Display() 方法被最先調(diào)用。
抽象類(lèi) VS 接口
到這里,我想你肯定有疑問(wèn),抽象類(lèi) 和 接口 是不是很相似了,甚至可以互換了?雖然抽象類(lèi)和接口現(xiàn)在看起來(lái)在很多方面都很相似,但兩者之間還是有微妙的區(qū)別的,具體如下:
- 抽象類(lèi)可以有實(shí)例成員,接口則不能。
- 抽象類(lèi)不能多繼承,接口還是可以的。
默認(rèn)接口方法 允許開(kāi)發(fā)人員利用 trait 編程技術(shù),該技術(shù)可以讓那些附屬于該方法的不相關(guān)類(lèi)型得以繼續(xù)使用,可能你有點(diǎn)懵,我舉個(gè)例子:假設(shè)你構(gòu)建好了一個(gè)dll,被很多的開(kāi)發(fā)人員所使用,現(xiàn)在你要發(fā)布該 dll 的新版本,比如說(shuō)往接口中添加了新方法,這個(gè)時(shí)候你可以定義默認(rèn)實(shí)現(xiàn),這樣就可以對(duì)已使用的開(kāi)發(fā)者進(jìn)行無(wú)感升級(jí)。
譯文鏈接:https://www.infoworld.com/article/3455239/how-to-use-default-interface-methods-in-csharp-8.html
總結(jié)
到此這篇關(guān)于在C# 8中如何使用默認(rèn)接口方法的文章就介紹到這了,更多相關(guān)C#8使用默認(rèn)接口方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vs2019 實(shí)現(xiàn)C#調(diào)用c++的dll兩種方法
這篇文章主要介紹了vs2019 實(shí)現(xiàn)C#調(diào)用c++的dll兩種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10C# Winfom 中ListBox的簡(jiǎn)單用法詳解
這篇文章主要介紹了C# Winfom 中ListBox的簡(jiǎn)單用法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12C#監(jiān)測(cè)IPv4v6網(wǎng)速及流量的實(shí)例代碼
這篇文章主要介紹了C#監(jiān)測(cè)IPv4v6網(wǎng)速及流量的實(shí)例代碼,文中講解非常細(xì)致,幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07C#可用于登錄驗(yàn)證碼的四位隨機(jī)數(shù)生成方法
這篇文章主要介紹了C#可用于登錄驗(yàn)證碼的四位隨機(jī)數(shù)生成方法,提供了兩種生成四位隨機(jī)數(shù)的方法供大家參考,是非常實(shí)用的技巧,需要的朋友可以參考下2014-12-12C#使用doggleReport生成pdf報(bào)表的方法
這篇文章主要介紹了C#使用doggleReport生成pdf報(bào)表的方法,結(jié)合實(shí)例形式分析了C# doggleReport安裝及使用具體操作技巧,需要的朋友可以參考下2017-06-06C#中創(chuàng)建PDF網(wǎng)格并插入圖片的方法
這篇文章我將向大家演示如何以編程的方式在PDF文檔中創(chuàng)建一個(gè)網(wǎng)格,并將圖片插入特定的網(wǎng)格中。對(duì)c# pdf 網(wǎng)格 插入圖片的知識(shí)感興趣的朋友一起看看吧2016-11-11WPF實(shí)現(xiàn)能自由改變形狀的四邊形和六邊形
這篇文章主要為大家詳細(xì)介紹了WPF如何實(shí)現(xiàn)能自由改變形狀的四邊形和六邊形,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-03-03