.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號: N170718502
出刊日期: 2017/7/26
C# 語言每一個新版本都提供許多新語法,讓程式撰寫的動作可以變得更簡潔,本文將介紹C# 7 新增的新語法,並利用一些範例來了解這些語法。
在方法參數列宣告out參數
C# 7新增一個新功能,可以在方法參數列直接宣告out參數,如此可以讓程式碼更容易閱讀,想要使用到out參數時便可以馬上宣告,不需另外撰寫一行變數宣告以接收out參數的值。
我們先看看在C# 6中out參數的宣告與使用,參考以下範例程式碼,叫用Rectangle方法計算出矩型面積,利用參數列上宣告的area out參數,將計算出的面積透過out參數回傳到呼叫端的area變數,你需要先寫一行宣告area的int型變數,再將它傳入Rectangle方法:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
int area;
Rectangle(l, w, out area);
Console.WriteLine($"{area}");
}
static void Rectangle(int len, int width, out int area)
{
area = len * width;
}
}
C# 7可以讓out參數的語法更簡潔,允許你在參數列(Argument List)傳遞out參數時一併宣告型別,不需要額外撰寫一行宣告變數的程式碼,例如改寫以上範例程式碼如下:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
Rectangle(l, w, out int area);
Console.WriteLine($"{area}");
}
static void Rectangle(int len, int width, out int area)
{
area = len * width;
}
}
此外當你在參數列使用out關鍵字宣告參數值,它的有效範圍會提升到外部範圍(Scope),因此我們可以在叫用Rectangle()方法的下一行,使用到area變數。不僅如此,C# 7也可以在參數列宣告out參數時利用var關鍵字由編譯器自動推論變數型別,例如以下程式碼:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
Rectangle(l, w, out var area);
Console.WriteLine($"{area}");
}
static void Rectangle(int len, int width, out int area)
{
area = len * width;
}
}
而在撰寫程式的過程,Visual Studio 2017也會提供提示的功能,顯示參數型別資訊,請參考下圖所示:

圖 1:Visual Studio 2017也會提供提示的功能。
Tuple物件
Tuple是一個簡單的資料結構,可以包含多個資料元素(Data Element),最早.NET Framework 4版就新增了Tuple物件,Tuple物件是一個有序的資料結構,可以儲存異質物件,但此版本提供的功能不太方便使用,要存取Tuple中的項目是透過Item1、Item2、Item3...的屬性語法,因此程式很容易誤判,或者寫錯。Tuple中可以存放多個項目,因此可以將它當做方法的回傳值,這是讓方法回傳多個值的一種解決方案。
例如以下C# 6的範例程式碼,在Rectangle方法建立一個Tuple物件,將矩型的面積與周長計算出來之後,透過Tuple回傳到呼叫端,在Main方法則用Item1與Item2屬性分別取得面積與周長的值:
class Program
{
static void Main(string[] args)
{
Tuple<int, int> result = Rectangle(40, 20);
Console.WriteLine($"Area = {result.Item1}");
Console.WriteLine($"Perimeter = {result.Item2}");
}
static Tuple<int, int> Rectangle(int len, int width)
{
//面積 = 長乘以寬
int area = len * width;
//周長 = (長+寬) * 2
int perimeter = (len + width) * 2;
return Tuple.Create(area, perimeter);
}
}
這個範例的執行結果參考如下:

圖 2:使用Tuple範例執行結果。
對於呼叫端而言,我們需要去記憶Item1代表的是面積,Item2代表的是周長,但當項目多時,就很容易搞錯,若要得到更多關於此版本的物件用法,請參閱本站一文,網址為:「http://blogs.uuu.com.tw/Articles/post/2016/05/04/Tuple簡介.aspx」
而C# 7 則將Tuple物件納入支援,在語法上則改得更為簡潔,以使用輕量型的Tuple資料結構來表達多個資料項目。不過,要使用Tuple之前,需要先在專案之中下載System.ValueTuple套件。
下載與安裝「System.ValueTuple」套件
使用Nuget套件管理員下載與安裝「System.ValueTuple」套件的步驟如下,從Visual Studio 2017開發工具「Solution Explorer」視窗選取專案名稱。再從「Tools」-「NuGet Package Manager」-「Package Manager Console」開啟「Package Manager Console」視窗,然後在提示字元中輸入install-package指令:
Install-Package System.ValueTuple
得到的執行結果如下圖所示:

圖 3:載與安裝「System.ValueTuple」套件。
使用Tuple物件
在C# 7中可以直接使用 ( ) 符號,以逗號區隔成員,將一到多個項目指定給Tuple物件,例如以下範例程式碼,將矩型的面積「800」與周長「120」指定給rectangle變數(它是一個Tuple物件),之後則可以利用Item1、Item2欄位語法將其中的項目讀出:
class Program
{
static void Main(string[] args)
{
var rectangle = (800, 120);
Console.WriteLine($"Area = {rectangle.Item1}");
Console.WriteLine($"Perimeter = {rectangle.Item2}");
}
}
這個範例的執行結果參考如下:

圖 4:使用Tuple物件。
當然在Tuple的小括號中,可以使用運算式進行一些計算,將運算的結果傳入Tuple物件,參考以下範例程式碼,這個範例的執行結果和上例一樣。
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
var rectangle = (l * w, (l + w) * 2);
Console.WriteLine($"Area = {rectangle.Item1}");
Console.WriteLine($"Perimeter = {rectangle.Item2}");
}
}
甚至在小括號中也可以呼叫方法將方法回傳值指派給Tuple物件,參考以下範例程式碼,這個範例的執行結果和上例一樣。
class Program
{
static void Main(string[] args)
{
int GetArea(int len, int width)
{
return len * width;
}
int GetPerimeter(int len, int width)
{
return (len + width) * 2;
}
int l = 40;
int w = 20;
var rectangle = (GetArea(l, w), GetPerimeter(l, w));
Console.WriteLine($"Area = {rectangle.Item1}");
Console.WriteLine($"Perimeter = {rectangle.Item2}");
}
}
設定Tuple項目名稱
使用C# 7建立Tuple物件時,可以為其中的項目指定一個有意義的名稱,這樣的好處是程式將會變得更容易閱讀,此外Visual Studio在開發過程中,也可以提供智慧型提示功能,來輔助程式碼的撰寫。例如以下範例程式碼,定義rectangle Tuple物件時,使用名稱設定它包含兩個欄位(field):area與perimeter,這樣要讀取它們的值時便可以使用你定義的名稱(如rectangle.area):
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
(int area, int perimeter) rectangle = (l * w, (l + w) * 2);
Console.WriteLine($"Area = {rectangle.area}");
Console.WriteLine($"Perimeter = {rectangle.perimeter }");
}
}
這些名稱只有在編譯階段存在,因此後續若透過反射(Reflection)機制在執行時期載入程式執行時,無法使用到這些名稱。另外在撰寫程式的過程中,Visual Studio將提供智慧型提示功能,避免程式寫錯:

圖 5:Visual Studio提供智慧型提示功能。
還有另一種為Tuple物件內含項目命名的寫法,在指派運算子(=)符號的右方,使用 ( ) 號設定Tuple項目時,直接搭配(:)號為其中的欄位取一個文字名稱,例如以下範例程式碼:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
var rectangle = (area: l * w, perimeter: (l + w) * 2);
Console.WriteLine($"Area = {rectangle.area}");
Console.WriteLine($"Perimeter = {rectangle.perimeter}");
}
}
若指派運算子(=)符號的左方與右方都同時取了名稱,此時右方的名稱將會被忽略,而以左邊設定的名稱為準,例如以下範例程式碼:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
(int Area, int Perimeter) rectangle = (area: l * w, perimeter: (l + w) * 2);
Console.WriteLine($"Area = {rectangle.Area}");
Console.WriteLine($"Perimeter = {rectangle.Perimeter}");
}
}
在方法中使用Tuple物件回傳值
以往在C# 6,方法只能回傳一個值,或要回傳多個值,需要透過回傳陣列、集合或自訂結構(Struct)自訂類別(Class),再或者利用前述的out參數、ref參數來達成。而在C# 7使用Tuple物件則可以很容易的從方法回傳多個值,而不需要再透過陣列、集合,或者自訂的結構(Struct)或自訂類別(Class)類別來回傳多個值。例如以下的範例程式碼,讓Rectangle方法回傳一個Tuple物件,其中包含兩個欄位記錄矩型的面積與周長的值。
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
var rectangle = Rectangle(l, w);
Console.WriteLine($"Area = {rectangle.Area}");
Console.WriteLine($"Perimeter = {rectangle.Perimeter}");
}
static (int Area, int Perimeter) Rectangle(int len, int width)
{
return (len * width, (len + width) * 2);
}
}
若Rectangle方法回傳型別定義為「(int, int)」,呼叫端就必須使用Item1、Item2語法來讀它的值,參考以下範例程式碼:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
var rectangle = Rectangle(l, w);
Console.WriteLine($"Area = {rectangle.Item1}");
Console.WriteLine($"Perimeter = {rectangle.Item2}");
}
static (int, int) Rectangle(int len, int width)
{
return (len * width, (len + width) * 2);
}
}
而以下在建立Tuple物件的 ( ) 號中設定名稱的寫法是無用的,因為設定的名稱會被方法回傳型別的「(int, int)」覆蓋,呼叫端還是得使用Item1、Item2語法來讀它的值:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
var rectangle = Rectangle(l, w);
Console.WriteLine($"Area = {rectangle.Item1}");
Console.WriteLine($"Perimeter = {rectangle.Item2}");
}
static (int, int) Rectangle(int len, int width)
{
return (area: len * width, perimeter: (len + width) * 2);
}
}
Deconstructing
有時我們需要將方法回傳的值指定給變數,若方法回傳Tuple物件,你可以利用Deconstructing語法,將Tuple物件中的多個欄位一次指派給多個變數。例如以下範例程式碼,將Rectangle方法回傳的Tuple物件之第一個欄位值指派給area變數;Tuple物件之第二個欄位值指派給perimeter變數:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
(int area, int perimeter) = Rectangle(l, w);
Console.WriteLine($"Area = {area}");
Console.WriteLine($"Perimeter = {perimeter}");
}
static (int Area, int Perimeter) Rectangle(int len, int width)
{
return (len * width, (len + width) * 2);
}
}
若自訂類別也想要提供Deconstructing功能,可以在類別中定義一個Deconstruct()方法,此方法利用一到多個out參數將運算後的值回傳。參考以下範例程式碼,Rectangle類別提供Deconstruct()方法將Area與Perimeter屬性的值粹取出來,放到呼叫端Main方法中的變數 area、perimeter:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
Rectangle r = new Rectangle(l, w);
(int area, int perimeter) = r;
Console.WriteLine($"Area = {area}");
Console.WriteLine($"Perimeter = {perimeter}");
}
}
public class Rectangle
{
public Rectangle(int len, int width)
{
this.Area = len * width;
this.Perimeter = (len + width) * 2;
}
public void Deconstruct(out int area, out int perimeter)
{
area = this.Area;
perimeter = this.Perimeter;
}
public int Area { get; set; }
public int Perimeter { get; set; }
}
也可以改寫如下,將建立(new)物件與解構賦值的程式寫在同一行:
class Program
{
static void Main(string[] args)
{
int l = 40;
int w = 20;
(int area, int perimeter) = new Rectangle(l, w);
Console.WriteLine($"Area = {area}");
Console.WriteLine($"Perimeter = {perimeter}");
}
}
public class Rectangle
{
public Rectangle(int len, int width)
{
this.Area = len * width;
this.Perimeter = (len + width) * 2;
}
public void Deconstruct(out int area, out int perimeter)
{
area = this.Area;
perimeter = this.Perimeter;
}
public int Area { get; set; }
public int Perimeter { get; set; }
}