[C#] C# 7 不能編譯? 其實是 Microsoft.Net.Compilers 版本問題

C# 7 隨著 Visual Studio 2017 出爐後,相信大家一定是躍躍欲試,好好的利用 C# 7 各式的語法糖 (誤) 來簡化寫碼的工作,但是如果你是由 Visual Studio 2015 升上來的 C# 專案,可能會遇到無法使用 C# 7 語法的問題,其實解決方案很簡單的...

C#,這個與 Microsoft .NET 平台一起誕生的程式語言,到了今天已經經過了 15 個年頭 (2002~2017),隨著功能的需求與競爭的需求 (別忘了它的對手是 Java),不斷的升級與更新,由 2.0 的泛型、匿名委派;3.5 的匿名型別,LINQ 與 lambda expression;4.0 的 TPL (Task Parallel Library) 到 4.5 的 async/await 等特性,讓使用 C# 的開發人員不用花費太多力氣就能寫出不錯的應用程式。

不過或許你不知道的是,C# 自 .NET 4.5.2 開始,因應 .NET Core 與跨平台的開發,C# 的編譯器悄悄的由 .NET Framework 大包中移出來了,首先是一個叫做 Microsoft Build Tools 的東西,能不用安裝大包的 Microsoft .NET Framework SDK 就能編譯 C# 與 Visual Basic .NET 等 Managed Language 所開發的程式,隨後在 2014 年時,微軟宣布了 Roslyn 計畫,重新打造 C# 與 Visual Basic 的編譯器,以作為新一代 C#/VB 的編譯平台,Visual Studio 2015 以及隨後的版本有很多與語言相關的功能都和 Roslyn 有關。

Roslyn 編譯平台隨後正名為 Microsoft .NET Compiler Platform,並開源在 GitHub,第一個版本隨著 Visual Studio 2015 一起出來,並且直接內建於 Web 專案範本,使用 Visual Studio 2015 新增的 ASP.NET 專案,Web.config 裡面會看到這玩意:

<system.codedom>
  <compilers>
    <compiler language="c#;cs;csharp" extension=".cs"
      type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
      warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701"/>
    <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
      type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
      warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+"/>
  </compilers>
</system.codedom>

Microsoft.CodeDom.Providers.DotNetCompilerPlatform 就是 Roslyn 編譯平台的組件,用以回報程式碼的資訊給編輯器使用,另外一個你看不到的,是它會在專案的 NuGet 參加中加入一個 Microsoft.Net.Compilers 的套件。

這也意味著,以後的 .NET Compiler 是可攜的,直接內建在專案內,事實上也是如此,你可以把有加入 Microsoft.Net.Compilers 的專案的 \bin 目錄打開,可以找到一個 roslyn 目錄,所有編譯器以及需要的檔案都在裡面了。

Roslyn 目前只有提供 C# 與 VB 的編譯器,其他語言的就隨緣了...

Roslyn 也帶來了 C# 6 以及 C# 7,未來還會有 C# 8 呢,其語言的版本會隨著 Microsoft.Net.Compilers 套件的版本來決定。

Microsoft.Net.Compilers 1.x: C# 6
Microsoft.Net.Compilers 2.x: C# 7

相信大家都有從網路上得知 C# 7 的資訊,也會利用各式活動 (例如 K.NET 曾辦過的 Connect Mini 2016 有介紹到 C# 7,以及 Visual Studio 94 狂的 C# 7 課程) 實際看到 C# 7 的能耐,但是一回家打開了 Visual Studio 2017,把之前在 Visual Studio 2015 建的專案打開,想試試 C# 7 的功能,例如下列程式碼使用了 ValueTuple 的功能:

public (int x, int y) GenerateRandomNumbers()
{
    var rnd = new Random();

    return new ValueTuple<int, int>(rnd.Next(), rnd.Next());
}
ValueTuple 是一個外掛功能,必須要安裝 System.ValueTuple NuGet 套件才可使用。

然後興高采烈的按了編譯,卻發現了一個殘酷的事實。

咦? 明明是用 Visual Studio 2017 開的,怎麼不能寫 C# 7 ? 

如果你從頭開始看的話,應該就知道原因了,Visual Studio 2017 不會管理 Roslyn 的版本,一切都由 Microsoft.Net.Compilers 套件管理,也就是說,你可以打開你的 NuGet 套件清單看看:

 

如果 Microsoft.Net.Compilers 版本還是 1.x,那很抱歉,它只能寫 C# 6 的語法,不過說到這裡,聰明的你應該知道怎麼做了吧,只要升級 Microsoft.Net.Compilers 套件到 2.x,C# 7 的大門就為你而開了

升級 Compiler 版本後,問題立馬消失。

順帶一提,因為 Visual Studio 2017 不再管理 .NET 編譯器,這也表示不是一定要 Visual Studio 2017 才能使用 C# 7,只要 Microsoft.Net.Compilers 可以運作 (.NET 4.5.1 以上),就能使用 C# 7 快樂的寫程式啦。

上面這句話有一個但書,專案必須是使用 Visual Studio 2017 建出來的專案,用 Visual Studio 2015 打開時才可以編譯,而且一樣是要升級 Microsoft.Net.Compilers 到 2.x。
至於原因是什麼,請參考另一篇文章:https://dotblogs.com.tw/regionbbs/2017/03/31/using-cs7-in-vs2015

Reference:

https://github.com/dotnet/roslyn