我在 stackoverflow 上面看到的一個問題,覺得有點意思。接著,爬了一些文,發現這個問題至少在三、四年前就有人反映,直到最近幾天都還有人在討論。所以我稍微整理一下這個問題的來龍去脈,就當作是〈C# 的唯讀自動屬性是怎樣煉成的〉續集吧。
問題描述
請先看底下這段程式碼:
說明:
剛才的程式碼沒有問題,可以通過編譯。這裡有 .NET Fiddle 的連結可以直接測試:
https://dotnetfiddle.net/y5l8aJ(必須把 Compiler 指定為 Roslyn 2.0)。
那麼,如果我們想要改成明確實作(explicitly implement)介面的寫法,直覺上可能會這樣寫:
倒數第 3 行無法通過編譯,錯誤訊息是:
那麼,把發生錯誤的那行程式碼改成底下這樣試試:
這也行不通,編譯器會告訴你:
可是,回頭再看一下第一個範例,當我們採用隱含實作介面的寫法時,Salary 也是定義成唯讀屬性啊;而且,自動唯讀屬性本來就可以在建構函式裡面設定初始值(C# 6)。顯然,這條語法規則一旦碰到明確實作介面的場合,就行不通了。
解決方法
目前看來,若一定要採用明確實作介面的寫法,就只能繞個彎,用唯讀的私有欄位來解決了。像這樣:
如果要再簡潔一點,可以用「以表示式為本體的成員」(expression-bodied members)語法:
小結
這篇筆記的重點是:
目前這個問題在 C# 語言設計的官方 github repo 上面仍有人在持續討論。有興趣的話,可以 follow 看看後續如何發展。
參考資料
問題描述
請先看底下這段程式碼:
interface IEmployee { int Salary { get; } // 唯讀屬性。 } class Employee : IEmployee { public int Salary { get; } // 隱含實作 IEmployee.Salary 屬性。 public Employee() { Salary = 70000; // 在建構函式中初始化唯讀屬性 Salary。 } }
說明:
- 介面 IEmployee 只定義了一個成員:Salary,它是個唯讀屬性。
- 類別 Employee 隱含實作了 IEmployee.Salary 屬性。由於它是唯讀自動屬性,故在建構函式中設定其初始值。
剛才的程式碼沒有問題,可以通過編譯。這裡有 .NET Fiddle 的連結可以直接測試:
https://dotnetfiddle.net/y5l8aJ(必須把 Compiler 指定為 Roslyn 2.0)。
那麼,如果我們想要改成明確實作(explicitly implement)介面的寫法,直覺上可能會這樣寫:
class Employee : IEmployee { int IEmployee.Salary { get; } // 明確實作 IEmployee.Salary 屬性。 public Employee() { Salary = 70000; // 編譯失敗: 此處無法使用 Salary! } }
倒數第 3 行無法通過編譯,錯誤訊息是:
The name 'Salary' does not exist in the current context. (在目前的區塊裡面不存在 'Salary' 這個東西。)這是因為明確實作介面的時候,不能在類別裡面直接以名稱來存取介面的成員。
那麼,把發生錯誤的那行程式碼改成底下這樣試試:
(this as IEmployee).Salary = 70000;
這也行不通,編譯器會告訴你:
Property or indexer 'IEmployee.Salary' cannot be assigned to -- it is read only.因為 Salary 本來就是定義成唯讀屬性,不可修改。
可是,回頭再看一下第一個範例,當我們採用隱含實作介面的寫法時,Salary 也是定義成唯讀屬性啊;而且,自動唯讀屬性本來就可以在建構函式裡面設定初始值(C# 6)。顯然,這條語法規則一旦碰到明確實作介面的場合,就行不通了。
解決方法
目前看來,若一定要採用明確實作介面的寫法,就只能繞個彎,用唯讀的私有欄位來解決了。像這樣:
class Employee : IEmployee { private readonly int _salary; // 唯讀的私有欄位。 int IEmployee.Salary // 明確實作 IEmployee.Salary 屬性。 { get { return _salary; } } public Employee() { _salary = 70000; } }
如果要再簡潔一點,可以用「以表示式為本體的成員」(expression-bodied members)語法:
class Employee : IEmployee { private readonly int _salary; // 唯讀的私有欄位。 int IEmployee.Salary => _salary; // 明確實作 IEmployee.Salary 屬性。 public Employee() { _salary = 70000; } }
小結
這篇筆記的重點是:
- 唯讀自動屬性可以在建構函式中設定初始值。這沒有違反 C# 語法。
- 如果採用明確實作介面的方式,上述作法行不通。
目前這個問題在 C# 語言設計的官方 github repo 上面仍有人在持續討論。有興趣的話,可以 follow 看看後續如何發展。
參考資料
沒有留言: