C# 8 的預設介面實作

這篇筆記是關於 C# 8 的新功能:預設介面實作(default interface implementation)。


預設介面實作(default interface implementation)是 C# 8 的新功能,它能夠讓你在介面中加入一些預設實作,包括方法、屬性、靜態欄位、巢狀型別等等。此功能需要靠 CLR 來實現,故只有當你的 .NET 專案的目標框架是 .NET Core 3.x 以上或 .NET Standard 2.1 以上的版本才能在程式中使用這個新語法。

那麼,C# 為什麼要增加這個語法呢?

我們知道,類別在實作某個介面的時候,一定要實作該介面所定義的所有成員。試著想像 C# 8.0 之前,也就是還沒有預設介面實作的時代,你設計的某個介面一旦對外公開了、有其他類別開始實作那個介面了,那麼介面就不可再更改 ——你不能修改既有的介面方法與屬性,也不能在那個介面中添加任何新的方法或屬性,否則原先已經實作該介面的類別便無法通過編譯(因為缺少了部分成員的實作)。這也是為什麼我們以前常聽到的一個原則:介面一旦公開了,就不可再修改。這形成了一種限制,導致介面的僵化,無法隨著時代和需求持續演進。

現在,C# 8 的預設介面實作放寬了上述限制,讓我們能夠對既有的介面添加新的成員。先來看一個簡單的例子:

public interface ILogger
{
    void Log(string msg);
}

如果某個類別要實作 ILogger,它就必須提供 Log 方法的實作:

public class Logger
{
    void Log(string msg) 
    { 
        Console.WriteLine(msg); 
    }
}

到目前為止,一切都沒問題,直到你想要在 ILogger 中增加新成員。此時,C# 8 的預設介面實作便可派上用場。假設你想要新增一個 Error 方法,便可以這樣寫:

public interface ILogger
{
    // 這是原本我們熟悉的、沒有實作的介面方法。
    void Log(string msg); 
    
    // 此方法提供了預設實作。C# 8 以後才能這樣寫。
    void Error(Exception ex) 
    { 
        Console.WriteLine(ex); 
    }
}

底下是用戶端的部分,跟以前的寫法完全一樣:

ILogger logger = new Foo();
logger.Error("....");

但是不能這樣寫:

Logger logger = new Logger();
logger.Error(exception);  // 編譯錯誤!

編譯錯誤的原因是,預設方法 Error 只存在介面裡,而不會被類別 Logger 繼承下來。

除了提昇介面的向後相容能力(backward compatibility),「預設介面實作」也帶來了更多新語法,例如在介面中定義常數、靜態欄位、巢狀型別等等。參考以下範例:

這些介面成員也都可以加上存取範圍修飾詞,例如上例中的巢狀型別,你可以根據實際需要來決定是否替它加上 privateprotected、或 public 修飾詞(預設為 public)。


技術提供:Blogger.