.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號: N171118903
出刊日期: 2017/11/29
本篇文章將介紹C# 7.1版新語法,包含非同步Main方法、Default Literal、推論Tuple名稱、泛型模式比對等等主題。
C# 專案屬性語言版本設定
預設在Visual Studio 2017建立專案時,C# 專案屬性的語言版本設定是「C# latest major version (default)」,若要使用到C# 7.1 新語法,必需更改這個設定值,請參考下圖所示。

圖 1:設定C# 專案屬性的語言版本。
我們以.NET Core 類型的主控台應用程式(Console)程式來說明如何設定C# 7.1 版語言版本,使用Visual Studio 2017建立一個「Console App(.NET Core)」專案,請參考下圖所示:

圖 2:建立C#測試專案。
使用滑鼠選取「Solution Explorer」視窗,在專案名稱上按滑鼠右鍵,從快捷選單中選取「Properties」選項,開啟屬性視窗。選取左方「Build」分頁,然後點選右方的「Advanced」按鈕,請參考下圖所示:

圖 3:設定C# 專案屬性的語言版本。
在「Advanced Build Settings」對話盒中,將「Language version」設定為「C# 7.1」,如此便可以使用新語法了,請參考下圖所示:

圖 4:設定C# 專案屬性的語言版本為7.1版。
接著我們來看看C# 7.1新增的新語法。
非同步Main方法
「Main」方法是C#程式碼的進入點,一個典型的C# Main方法如下程式碼:
class Program
{
static void Main(string[] args)
{
}
}
「Main」方法可以回傳數值型別,通常以數值來表示程式執行結果,參考以下範例程式碼:
class Program {
static int Main(string[] args) {
return 0;
}
}
「Main」方法可以沒有參數,例如以下程式碼範例:
class Program
{
static int Main()
{
return 0;
}
}
「Main」方法可以沒有參數,回傳int型別,例如以下程式碼範例:
class Program
{
static int Main()
{
return 0;
}
}
在C# 7.1版之前,「Main」方法不可以加註「async」關鍵字,例如在C# 7版之前若「Main」方法加註「async」關鍵字:
static async Task Main( string[] args ) {
}
則編譯程式碼時,會出現以下錯誤訊息:
CS5001 C# Program does not contain a static 'Main' method suitable
C# 7.1 版現在支援了非同步「Main」方法,可以在方法中搭配「await」關鍵字等待非同步的工作,參考以下程式碼範例,在非同步「Main」方法中利用「HttpClient」類別下載網站首頁內容,然後在方法中使用「await」關鍵字等待執行結果:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace CS71 {
class Program {
static async Task Main( string[] args ) {
HttpClient client = new HttpClient( );
Task<string> mytask = client.GetStringAsync( "http://www.microsoft.com" );
string page = await mytask;
Console.WriteLine( page );
}
}
}
此範例的執行結果請參考下圖所示:

圖 5:非同步Main方法。
Main方法可以回傳Task<T>,搭配async await關鍵字,例如以下範例程式碼:
class Program {
static async Task<int> Main( string[] args ) {
HttpClient client = new HttpClient( );
Task<string> mytask = client.GetStringAsync( "
http://www.microsoft.com" );
string page = await mytask;
Console.WriteLine( page.Length );
return page.Length;
}
}
Main方法可以回傳Task<T>,例如以下範例程式碼:
class Program {
static Task<int> Main( string[] args ) {
HttpClient client = new HttpClient( );
Task<string> mytask = client.GetStringAsync( "
http://www.microsoft.com" );
string page = mytask.Result;
Console.WriteLine( page.Length );
return Task.FromResult( page.Length ) ;
}
}
Default Literal
通常參考型別初始化時,會設為「null」;實值型別初始化時會設定為特定的值,如數值型別設定為「0」,Default Literal用來取得特定型別的預設值,此型別可以是實值型別(Value Type)或是參考型別(Reference Type)。在前版C# 要取得型別預設值,可以利用「default(T)」語法,例如以下範例程式碼:
string d1 = default( string );
Console.WriteLine( String.IsNullOrEmpty( d1 ) ); // true
Program d2= default( Program );
Console.WriteLine( d2 == null ); // true
bool d3 = default( bool );
Console.WriteLine( d3 ); // false
int d4 = default( int );
Console.WriteLine( d4 ); // 0
「default」關鍵字特別適用於使用var關鍵字宣告變數的情境,例如以下範例程式碼:
var d1 = default( string );
Console.WriteLine( String.IsNullOrEmpty( d1 ) ); // true
var d2 = default( Program );
Console.WriteLine( d2 == null ); // true
var d3 = default( bool );
Console.WriteLine( d3 ); // false
var d4 = default( int );
Console.WriteLine( d4 ); // 0
現在C# 7.1 版可以直接使用「default」關鍵字,不需要再「default」關鍵字之後再描述型別,讓程式碼更為簡單,例如以下範例程式碼:
string d1 = default;
Console.WriteLine( String.IsNullOrEmpty( d1 ) ); // true
Program d2= default;
Console.WriteLine( d2 == null ); // true
bool d3 = default;
Console.WriteLine( d3 ); // false
int d4 = default;
Console.WriteLine( d4 ); // 0
但不能夠搭配var關鍵字使用,以下程式碼將無法編譯:
var d1 = default; // Error
推論Tuple名稱
C# 7為Tuple加入許多新功能,可以參考本站《C# 7新功能概覽 - 1》一文的說明。這個新功能就根本節的標題一樣自動推論Tuple項目的名稱,參考以下範例程式碼建立一個newRect匿名型別,它的屬性值來自於「Rectangle」型別的「r」物件,預設newRect匿名型別就會自動產生兩個和Rectangle屬性同名的屬性「Area」與「Perimeter」,然後我們就可以利用「newRect.Area」與「newRect.Perimeter」語法來存取他們的值:
class Program {
static void Main( string[] args ) {
int l = 40;
int w = 20;
Rectangle r = new Rectangle( l , w );
Console.WriteLine( $"Area = {r.Area}" );
Console.WriteLine( $"Perimeter = {r.Perimeter}" );
var newRect = new { r.Area , r.Perimeter};
Console.WriteLine( $"Area = {newRect.Area}" );
Console.WriteLine( $"Perimeter = {newRect.Perimeter}" );
}
}
public class Rectangle {
public Rectangle( int len , int width ) {
this.Area = len * width;
this.Perimeter = ( len + width ) * 2;
}
public int Area { get; set; }
public int Perimeter { get; set; }
}
此範例的執行結果請參考下圖所示:

圖 6:推論Tuple名稱。
C# 7.1 版可以改寫如下,建立一個Tuple(newRect),其項目的值來自於Rectangle物件,其項目的名稱自動推論:
class Program {
static void Main( string[] args ) {
int l = 40;
int w = 20;
Rectangle r = new Rectangle( l , w );
Console.WriteLine( $"Area = {r.Area}" );
Console.WriteLine( $"Perimeter = {r.Perimeter}" );
var newRect = ( r.Area , r.Perimeter);
Console.WriteLine( $"Area = {newRect.Area}" );
Console.WriteLine( $"Perimeter = {newRect.Perimeter}" );
}
}
public class Rectangle {
public Rectangle( int len , int width ) {
this.Area = len * width;
this.Perimeter = ( len + width ) * 2;
}
public int Area { get; set; }
public int Perimeter { get; set; }
}
使用Tuple的好處是,它是實值型別,比起匿名型別執行效能要來的好。
泛型模式比對
C# 7 新增模式比對(Pattern Matching)語法,但不支援泛型的比對,C# 7.1 新的泛型模式比對(Pattern Matching With Generics)則改良原有語法,舉例來說,參考以下範例程式碼:
class Program {
static void Main( string[] args ) {
List<Employee> employees = new List<Employee>( ) {
new Sales { Id = 1 , Name = "Mary" , Bonus = 1000 } ,
new Sales { Id = 2 , Name = "Candy" , Bonus = 2000 } ,
new CEO { Id = 3 , Name = "Candy" , Allowance = 10000 }
};
foreach ( var emp in employees ) {
ShowInfo( emp );
}
}
static void ShowInfo<T>( T t ) where T : Employee {
if ( t is Sales s ) {
Console.WriteLine( $"Sales : ( {t.Id} , {t.Name} ) , Bonus : {s.Bonus}" );
}
else if ( t is CEO c ) {
Console.WriteLine( $"CEO : ( {t.Id} , {t.Name} ) , Allowance : {c.Allowance}" );
}
}
}
public abstract class Employee {
public int Id { get; set; }
public string Name { get; set; }
}
public class Sales : Employee {
public int Bonus { get; set; }
}
public class CEO : Employee {
public int Allowance { get; set; }
}
此範例的執行結果請參考下圖所示:

圖 7:泛型模式比對。
上述的ShowInfo<T>() 方法有一個泛型型別參數,ShowInfo<T>() 方法中使用到generic pattern matching語法,使用is樣式運算式(Pattern Expression)來比對型別,並隱含轉換成Sales或CEO型別。這段程式碼在C# 7 編譯時,會得到以下錯誤訊息:
An expression of type 'T' cannot be handled by a pattern of type 'Sales' in C# 7. Please use language version 7.1 or greater.