C# 6 新語法 - 2

by vivid 1. 七月 2015 13:39

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:N150716201
出刊日期:2015/7/1
開發工具:Visual Studio 2015 RC
版本:.NET Framework 4.6、C# 6

在這篇文章之中,我將介紹C# 6程式語言所提供的新語法。因為Visual Studio 2015中文版還未問市,因此在此篇文章之中,專有名詞的部分就儘量使用原文。

Auto-Properties Initializers(自動屬性初始設定式)

C# 5版之前,若要為自動屬性設定值,你可能需要額外撰寫一些程式碼,例如透過建構函式來初始化自動屬性的值。參考以下範例程式碼,Employee類別中包含ID、Name兩個自動屬性,在建構函式中,設定它們的值:

using System;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      var emp = new Employee(1,"Mary");
      Console.WriteLine ( emp.ID );  //1
      Console.WriteLine ( emp.Name ); //Mary
    }
  }
  class Employee {
    public Employee ( ) {
    }
    public Employee ( int id , string name ) {
      ID = id;
      Name = name;
    }
    public int ID { get; set; }
    public string Name { get; set; }
  }
}


 

在C# 6版之後,使用自動屬性語法時,可以宣告順帶初始化屬性的值,只要宣告時加上「=」等號,再給個值就行了,參考以下範例程式碼:

using System;
using System.IO;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      var emp = new Employee();
      Console.WriteLine ( emp.ID );  //1
      Console.WriteLine ( emp.Name ); //Mary
    }
  }
  class Employee {
    public int ID { get; set; } = 1;
    public string Name { get; set; } = "Mary";
  }
}

 

唯讀自動屬性(Getter-only auto-properties)

在C# 5,自動屬性一定是可讀寫的,以下程式碼片段是C# 5的自動屬性語法,Company類別中包含一個Name的自動屬性,在定義時一定要宣告成可讀寫,也就是說一定要加入get與set存取子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      Company p = new Company();
      p.Name = "MyCompany";
      Console.WriteLine ( p.Name );
 
    }
  }
  class Company {
    public string Name { get; set; }
  }
 
}

 

若省略任何一個存取子,都會造成編譯錯誤,例如以下程式碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      Company p = new Company();
      p.Name = "MyCompany";
      Console.WriteLine ( p.Name );
 
    }
  }
  class Company {
    public string Name { get; }
  }
 
}

 

編譯時將出現錯誤訊息如下:

clip_image002

圖 1:在C# 5,自動屬性一定是可讀寫的。

在C# 6允許設計唯讀自動屬性,並利用 Auto-Properties Initializers 進行初始化,改寫上述範例程式碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      Company p = new Company();
      Console.WriteLine ( p.Name );

    }
  }
  class Company {
    public string Name { get; } = "MyCompany";
  }
}

 

同時也可和唯讀變數一樣,你可以在建構函式之中進行初始化的動作:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      Company p = new Company("MyCompany");
      Console.WriteLine ( p.Name ); // MyCompany
    }
  }
  class Company {
    public string Name { get; }
    public Company ( string n ) {
      Name = n;
    }
  }
}

 

除了在建構函式之中可以修改唯讀屬性的值之外,不能夠在其他程式碼試圖修改它的值,Visual Studio 2015 RC將會提示錯誤,請參考下圖所示:

clip_image004

圖 2:不能夠在其他程式碼試圖修改它的值。

Expression-bodied member

在C# 5我們可以為類別定義方法,例如以下程式碼為Employee類別定義一個GetName()方法:

using System;
class Program {
  static void Main( string[ ] args ) {
    Employee emp = new Employee( );
    emp.LastName = "Lee";
    emp.FirstName = "Mary";
    Console.WriteLine( emp.GetName( ) );  // Mary , Lee
  }
}
class Employee {
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string GetName( ) {
    return FirstName + " , " + LastName;
  }
}

 

在C#6定義類別的方法(Method)與屬性(Property)時,可以直接使用Lambda Expression,讓程式又變短了,此語法稱之為Expression-bodied member。

將上述程式碼改用C# 6 Expression-bodied member語法撰寫如下,在「=>」符號右方直接回傳字串,連return關鍵字都可以省略:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      Employee emp = new Employee( );
      emp.LastName = "Lee";
      emp.FirstName = "Mary";
      Console.WriteLine ( emp.GetName ( ) ); //Mary , Lee
    }
  }
  class Employee {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string GetName ( ) => FirstName + " , " + LastName;
  }
}

 

搭配C# 6 字串格式化功能來使用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      Employee emp = new Employee( );
      emp.LastName = "Lee";
      emp.FirstName = "Mary";
      Console.WriteLine ( emp.GetName ( ) ); //Name is Mary , Lee
    }
  }
  class Employee {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string GetName ( ) => $"Name is {FirstName} , {LastName}";
  }
}

 

C# 5之前,定義屬性時,要規規矩矩地加上get或set方法,例如以下FullName屬性:

using System;
class Program {
  static void Main( string[ ] args ) {
    Employee emp = new Employee( );
    emp.LastName = "Lee";
    emp.FirstName = "Mary";
    Console.WriteLine( emp.GetName( ) );  // Mary , Lee
    Console.WriteLine( emp.FullName ); // Mary , Lee
  }
}
class Employee {
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string GetName( ) {
    return FirstName + " , " + LastName;
  }
  public string FullName {
    get {
      return this.FirstName + " , " + this.LastName;
    }
  }
 
}

 

而C# 6可以使用Lambda語法來改寫,稱之為Expression-bodied property,改寫上例程式碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      Employee emp = new Employee( );
      emp.LastName = "Lee";
      emp.FirstName = "Mary";
      Console.WriteLine ( emp.GetName ( ) ); // Mary , Lee
      Console.WriteLine (emp.FullName); // Mary , Lee
    }
  }
  class Employee {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string GetName ( ) => FirstName + " , " + LastName ;   
    public string FullName => this.FirstName + " , " + this.LastName;         
  }
}

 

Nameof運算子

有時程式執行發生例外錯誤,我們想要提醒程式設計師錯誤是來自於一個參數,以便於程式除錯,如以下程式碼所示,在開啟檔案時,若發生找不到檔案的例外錯誤,在訊息中將提示fileName參數設定有誤:

using System;
using System.IO;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      OpenFile ( @"c:\temp\b.txt" );
    }

    static void OpenFile ( string fileName ) {
      if ( !File.Exists ( fileName ) ) {
        throw new FileNotFoundException ( "File Not Found! check fileName parameter" , fileName );
      } else {
        var s =File.ReadAllText ( fileName );
        Console.WriteLine ( s );
      }
    }
  }
}

 

程式在執行時若發生例外,將顯示以下的錯誤訊息:

Unhandled Exception: System.IO.FileNotFoundException: File Not Found! check file

Name parameter

參考執行結果如下圖:

clip_image006

圖 3:執行例外。

萬一有一天程式碼因進行最佳化或程式重構的關係,fileName變數名稱被改掉了,而例外錯誤訊息中忘了更改,此時印出的訊息就牛頭不對馬嘴,程式中並不存在一個fileName參數:

using System;
using System.IO;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      OpenFile ( @"c:\temp\b.txt" );
    }

    static void OpenFile ( string fs) {
      if ( !File.Exists ( fs ) ) {
        throw new FileNotFoundException ( "File Not Found! check fileName parameter" , fs );

      } else {
        var s =File.ReadAllText ( fs );
        Console.WriteLine ( s );
      }
    }
  }
}

 

為了避免這種情況,我們可以利用nameof運算子來取得變數的名稱,例如改寫程式碼如下:

using System;
using System.IO;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      OpenFile ( @"c:\temp\b.txt" );
    }

    static void OpenFile ( string fileName ) {
      if ( !File.Exists ( fileName ) ) {
        throw new FileNotFoundException ( "File Not Found! check "
          + nameof ( fileName ) + " parameter" , fileName );

      } else {
        var s =File.ReadAllText ( fileName );
        Console.WriteLine ( s );
      }
    }
  }
}

 

得到的錯誤訊息如下:

Unhandled Exception: System.IO.FileNotFoundException: File Not Found! check file

Name parameter

但若修改變數名稱為fs

using System;
using System.IO;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {

      OpenFile ( @"c:\temp\b.txt" );
    }

    static void OpenFile ( string fs ) {
      if ( !File.Exists ( fs ) ) {
        throw new FileNotFoundException ( "File Not Found! check "
          + nameof ( fs ) + " parameter" , fs );

      } else {
        var s =File.ReadAllText ( fs );
        Console.WriteLine ( s );
      }
    }
  }
}

 

得到的錯誤訊息能正確地反應:

Unhandled Exception: System.IO.FileNotFoundException: File Not Found! check fs parameter

同樣的,我們也可以搭配字串格式化的新語法:

using System;
using System.IO;

namespace CS6 {
  class Program {
    static void Main ( string [ ] args ) {
      OpenFile ( @"c:\temp\b.txt" );
    }
    static void OpenFile ( string fileName ) {
      if ( !File.Exists ( fileName ) ) {
        throw new FileNotFoundException ( $"File Not Found! check {nameof ( fileName )} parameter." );

      } else {
        var s =File.ReadAllText ( fileName );
        Console.WriteLine ( s );
      }
    }
  }
}

Tags:

.NET Magazine國際中文電子雜誌 | C#

新增評論




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






NET Magazine國際中文電子雜誌

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

月分類Month List