C# 7新功能概覽 - 1

by vivid 26. 七月 2017 15:47

.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也會提供提示的功能,顯示參數型別資訊,請參考下圖所示:

clip_image002

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

 

這個範例的執行結果參考如下:

clip_image004

圖 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

得到的執行結果如下圖所示:

clip_image006

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

這個範例的執行結果參考如下:

clip_image008

圖 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將提供智慧型提示功能,避免程式寫錯:

clip_image010

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

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