新聞中心
本文轉載自微信公眾號「碼農讀書」,作者碼農讀書 。轉載本文請聯系碼農讀書公眾號。

成都創(chuàng)新互聯公司是由多位在大型網絡公司、廣告設計公司的優(yōu)秀設計人員和策劃人員組成的一個具有豐富經驗的團隊,其中包括網站策劃、網頁美工、網站程序員、網頁設計師、平面廣告設計師、網絡營銷人員及形象策劃。承接:成都做網站、網站建設、網站改版、網頁設計制作、網站建設與維護、網絡推廣、數據庫開發(fā),以高性價比制作企業(yè)網站、行業(yè)門戶平臺等全方位的服務。
C# 8 中新增了一個非常有趣的特性,叫做 默認接口方法 (又稱虛擬擴展方法),這篇文章將會討論 C# 8 中的默認接口方法以及如何使用。
在 C# 8 之前,接口不能包含方法定義,只能在接口中定義方法簽名,還有一個就是接口的成員默認是 public 和 abstract , 在 C# 8 之前,接口不能包含字段,也不能包含private, protected, 或者 internal 的方法成員。如果你在接口中引入了一個新成員,默認情況下你必須更新實現該接口的所有子類。
在 C# 8 中可以在接口定義方法的默認實現,而且還可以定義接口成員為 private,protect,甚至是 static,還有一點挺奇葩的,一個接口的 protect 成員是不能被實現類所訪問的,相反,它只能在子接口中被訪問,接口的 virtual 成員可以由派生接口 override,但不能被派生類 override,還有一點請注意,接口目前還不能定義 實例成員。
為什么要使用默認接口方法
所謂的 默認接口方法 指的是接口中定義了一個默認實現的方法, 如果實現該接口的類沒有實現默認接口方法的話,那么這個 默認接口方法 只能從接口上進行訪問,這是一個很有用的特性,因為它可以幫助開發(fā)人員在不破壞現有功能的情況下向接口的未來版本添加新方法。
考慮下面的 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
- }
- }
默認接口方法不能被繼承
現在創(chuàng)建一個 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# 將會使用最近覆蓋規(guī)則,即 Class.Display() 方法被最先調用。
抽象類 VS 接口
到這里,我想你肯定有疑問,抽象類 和 接口 是不是很相似了,甚至可以互換了?雖然抽象類和接口現在看起來在很多方面都很相似,但兩者之間還是有微妙的區(qū)別的,具體如下:
- 抽象類可以有實例成員,接口則不能。
- 抽象類不能多繼承,接口還是可以的。
默認接口方法 允許開發(fā)人員利用 trait 編程技術,該技術可以讓那些附屬于該方法的不相關類型得以繼續(xù)使用,可能你有點懵,我舉個例子:假設你構建好了一個dll,被很多的開發(fā)人員所使用,現在你要發(fā)布該 dll 的新版本,比如說往接口中添加了新方法,這個時候你可以定義默認實現,這樣就可以對已使用的開發(fā)者進行無感升級。
譯文鏈接:https://www.infoworld.com/article/3455239/how-to-use-default-interface-methods-in-csharp-8.html
本文名稱:如何在C#8中使用默認接口方法
本文地址:http://fisionsoft.com.cn/article/djegjie.html


咨詢
建站咨詢
