C# 7.1新功能概覽

by vivid 29. 十一月 2017 11:35

.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 新語法,必需更改這個設定值,請參考下圖所示。

clip_image002

圖 1:設定C# 專案屬性的語言版本。

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

clip_image004

圖 2:建立C#測試專案。

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

clip_image006

圖 3:設定C# 專案屬性的語言版本。

在「Advanced Build Settings」對話盒中,將「Language version」設定為「C# 7.1」,如此便可以使用新語法了,請參考下圖所示:

clip_image008

圖 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 );
    }
  }
}

 

此範例的執行結果請參考下圖所示:

clip_image010

圖 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; }
}


此範例的執行結果請參考下圖所示:

clip_image012

圖 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; }
}

 

此範例的執行結果請參考下圖所示:

clip_image014

圖 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.

Tags:

.NET Magazine國際中文電子雜誌 | C# | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List