Roslyn/C# 6 的一個小小改進,還真不太容易發現...

我用 Visual Studio 2015 在專案中加了一個 struct,沒有花太多腦筋而寫出底下這段程式碼:

public struct DateTimeOffsetRange
{
    public DateTimeOffsetRange(DateTimeOffset from, DateTimeOffset to)
    {
        From = from;
        To = to;
    }
    public DateTimeOffset From { get; private set; }
    public DateTimeOffset To { get; private set; }
}

如果你覺得似乎哪裡怪怪的,那你應該很常用 Visual Studio 2013(或更早的版本)撰寫自訂 struct。

老實說我很少寫 struct,故對於撰寫 struct 時的一些細節和限制,我並不是那麼清楚。我在寫剛才那段程式碼的時候,只是覺得理所當然。

確實,用 Visual Studio 2015 編譯和執行都沒問題。

可是 push 至遠端 Git repository 之後,build sever 寄信通知我編譯失敗。建構函式裡面的兩行程式碼都無法通過編譯,錯誤訊息是:

The 'this' object cannot be used before all of its fields are assigned to.

意思是:你必須為此型別的所有欄位都指定初始值之後,才能使用 this 來存取物件的屬性。

由於 builder server 是使用 Visual Studio 2013 來編譯專案,故猜想或許是 Visual Studio 2015 新的編譯器或 C# 6 新語法所造成的差異吧。於是進入專案的選項設定,把 C# 語言版本從 default(即 C# 6)改成 C# 5。結果編譯還是成功。甚至於把 C# 語言版本改成 C# 4 都沒問題!(見鬼了嗎?)

那麼,應該可以確定是 VS2015 中的編譯器(Roslyn)跟 VS2013 的編譯器所造成的差異了,儘管這看起來是 C# 6 在 auto-property initializer 語法的改進

若有興趣,你可以到 .NET Fiddle 上面測試看看,切換左邊的 Compiler 版本來觀察編譯結果。

順手整理一下,當我們用 C# 5 撰寫一個 struct,而且是 immutable struct(即物件一旦建立,外界就無法再更改其內部狀態)的時候,語法上需要注意的規則如下:
  • 必須明白宣告唯讀的、私有的 backing field。
  • 必須在建構函式中設定 backing field 的值。
  • 必須撰寫屬性的 getter 方法,而不能使用更簡潔的自動屬性語法。
  • 由屬性的 getter 方法回傳其 backing field 的值。

其實還挺麻煩的,不是嗎?

如果用 Visual Studio 2015 和 C# 6 就方便多了。
Happy coding!