C# 7新功能概覽 - 2

by vivid 9. 八月 2017 16:02

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:
N170818601
出刊日期: 2017/8/9

本文將延續本站《C#7新功能概覽 - 1》一文的說明,介紹C# 7 新增的新語法,並利用一些範例來了解這些語法。

Pattern matching

C# 7的Pattern matching可以根據特定的類別或是結構來進行比對,看看比對的結果是否符合特定的樣式。Pattern matching功能支援兩個運算式(Expression):is與switch。這些運算式可以檢視物件與物件的屬性,來判斷物件是否滿足某種樣式(Pattern),還可以搭配when關鍵字以指定樣式的相關規則。

is樣式運算式(Pattern Expression

is樣式運算式(Pattern Expression)擴充原有的is運算子(Operator)的功能,以根據型別來查詢物件。

例如以下範例程式碼,在集合中存放數值與包含NT$貨幣符號的字串,我們希望將計算出所有項目的加總,若集合中存放的是數值,直接累加到total,若集合中存放的是帶貨幣符號的字串,則先將字串根據台灣文特性來轉型,粹取出數值,再和total進行累加,在C# 6可以撰寫這樣的程式碼來達到這個需求:

class Program
{
    static void Main(string[] args)
    {
        int x = 100;
        string y = "NT$200";
        IEnumerable<object> data = new List<object>() { x, y };
        int total = 0;

        foreach (var item in data)
        {
            if (item is int)
            {
                total += (int)item;
            }
            else if (item is string && item.ToString().Contains("NT$"))
            {
                total += int.Parse(item.ToString(), NumberStyles.Currency, new CultureInfo("zh-TW"));
            }
        }

        Console.WriteLine(total);
    }

}

 

此範例的執行結果會印出累計的結果「300」,而在C# 7可以改寫如下:

class PatternMatchingDemo
{
    static void Main(string[] args)
    {
        int x = 100;
        string y = "NT$200";
        IEnumerable<object> data = new List<object>() { x, y };
        int total = 0;

        foreach (var item in data)
        {
            if (item is int i)
            {
                total += i;
            }
            else if (item is string s && s.Contains("NT$"))
            {
                total += int.Parse(item.ToString(), NumberStyles.Currency, new CultureInfo("zh-TW"));
            }
        }

        Console.WriteLine(total);
    }
}

 

比對程式碼,在C# 7 if判斷式中,使用is比對集合中的項目的型別若為「int」,可以直接將item指定一個變數名稱「i」,在if的區塊中使用它。而第二個if 則利用一行程式碼,完成兩個動作,首先在判斷集合中的項目是否是「string」型別,若是字串型別,則自動將其轉型成字串,並指定給s變數。因此不需要像C# 6 還需要明確叫用ToString()方法將物件轉型成字串。

若集合中項目存放的是「null」時,要將total做累加一動作,例如以下將z變數放到集合中,此時就可以利用is運算子進行判斷:

class PatternMatchingDemo
{
    static void Main(string[] args)
    {
        int x = 100;
        string y = "NT$200";
        int? z = null;
        IEnumerable<object> data = new List<object>() { x, y, z };
        int total = 0;

        foreach (var item in data)
        {
            if (item is null)
            {
                total += 1;
            }
            else if (item is int i)
            {
                total += i;
            }
            else if (item is string s && s.Contains("NT$"))
            {
                total += int.Parse(item.ToString(), NumberStyles.Currency, new CultureInfo("zh-TW"));
            }
        }

        Console.WriteLine(total);
    }
}

 

switch式運算式(Pattern Expression)

若要判斷的條件有很多,不同的條件要分支去執行不同的程式碼,使用switch語法會比if語法看起來更為簡潔。在新的C# 7 switch語法,有著顯著的改善,可以撰寫類似VB的Select Case語法,更容易判斷資料是否在一個範圍,或符合特定條件。

以往在C# 6時,當運算分支的邏輯不是只拿一個簡單的型別做比對時,只能使用if - else if語法,現在C# 7使用新的Pattern matching語法可以讓程式更容易達到這樣的要求。switch陳述式與 case關鍵字之後的運算式現在不限定在只能使用數值(如int),或字串型別,可以包含變數或運算式,也可以使用到.NET型別或自訂類別。若在case關鍵字之後使用到「null」,則「null」將視為一個常數運算式(constant expresion),可以匹配null參考型別物件(null reference-type object)。

參考以下範例程式碼,從主控台輸入一個課程的成積,並透過ParseNullable方法,將其轉型成數值印出:

class Program
{
    public static int? ParseNullable(string val)
    {
        int output;
        return int.TryParse(val, out output) ? (int?)output : null;
    }
    static void Main(string[] args)
    {
        Console.WriteLine("請輸入一個數字");
        var scores = ParseNullable(Console.ReadLine());

        Console.WriteLine("中文");
        switch (scores)
        {
            case 100:
                Console.WriteLine("滿分");
                break;
            case var s when (s == 0):
                Console.WriteLine("零分");
                break;
            case var s when (s >= 99 & s <= 60):
                Console.WriteLine("及格");
                break;
            case var s when (s < 60 & s > 0):
                Console.WriteLine("不及格");
                break;
            default:
                Console.WriteLine("不正確的成績");
                break;
        }

    }
}

 

在第一個case區塊中,將數值與常數「100」做比對,而第二個case則在後面的運算式中,使用when關鍵字加上判斷條件,在成績(scores)為「0」時印出「零分」。第三個case則判斷成績是否在 99~60分之間;最後一個是預設值,因此若輸入負數和英文文字,如「a」,則會印出「不正確的成績」。

若輸入負數和英文文字要執行不同的程式邏輯,我們可以在switch的case區段判斷null值,

參考以下範例程式碼:

class Program
{
    public static int? ParseNullable(string val)
    {
        int output;
        return int.TryParse(val, out output) ? (int?)output : null;
    }
    static void Main(string[] args)
    {
        Console.WriteLine("請輸入一個數字");
        var scores = ParseNullable(Console.ReadLine());

        Console.WriteLine("中文");
        switch (scores)
        {
            case 100:
                Console.WriteLine("滿分");
                break;
            case var s when (s == 0):
                Console.WriteLine("零分");
                break;
            case var s when (s >= 99 & s <= 60):
                Console.WriteLine("及格");
                break;
            case var s when (s < 60 & s > 0):
                Console.WriteLine("不及格");
                break;
            case var s when s is null:
                Console.WriteLine("null");
                break;
            default:
                Console.WriteLine("不正確的成績");
                break;
        }

    }
}

 

此範例若輸入負數印出「不正確的成績」,若輸入英文文字,根據ParseNullable方法的邏輯,若轉型失敗ParseNullable方法會回傳null,因此switch程式區段最後會印出「null」。

特別注意,在switch使用Pattern Expression時,case出現的順序會影響程式的執行,規則比較嚴格或特殊的case區塊,程式的位置要出現在比較上面。我們將前面範例改簡單一些,參考以下範例程式碼:

class Program
{
    public static int? ParseNullable(string val)
    {
        return int.TryParse(val, out int output) ? (int?)output : null;
    }
    static void Main(string[] args)
    {
        Console.WriteLine("請輸入一個數字");
        var scores = ParseNullable(Console.ReadLine());

        Console.WriteLine("中文");
        switch (scores)
        {
            case 100:
                Console.WriteLine("滿分");
                break;
            case var s when (s > 0):
                Console.WriteLine(s);
                break;
            default:
                Console.WriteLine("不正確的成績");
                break;
        }

    }
}

 

若輸入100則印出「滿分」;若輸入非100的正數值,就印出此數值,但若修改程式碼順序如下:

class Program
{
    public static int? ParseNullable(string val)
    {
        return int.TryParse(val, out int output) ? (int?)output : null;
    }
    static void Main(string[] args)
    {
        Console.WriteLine("請輸入一個數字");
        var scores = ParseNullable(Console.ReadLine());

        Console.WriteLine("中文");
        switch (scores)
        {
            case var s when (s > 0):
                Console.WriteLine(s);
                break;
            case 100:
                Console.WriteLine("滿分");
                break;
            default:
                Console.WriteLine("不正確的成績");
                break;
        }

    }
}

 

因為第二個case涵蓋在第一個case的範圍內,因此Visual Studio會顯示一個錯誤,無法編譯程式碼:

clip_image002

圖 1:switch case出現的順序會影響程式的編譯。

比對型別(Type )

在每一個「case」後的運算式(Expression)可以包含一個型別(Type)名稱再加一個變數名稱,根據型別來比對是否滿足條件,此變數的有效範圍限定在此case區塊之中。Null實體永遠不會匹配這些型別判斷的case區塊,若要判斷是否為Null值,直接使用一個「null」 實體即可,參考以下範例程式碼:

class PatternMatchingDemo
{
     static void Main(string[] args)
     {
         Employee employee1 = new Employee() { Name = "Mary" };
         Employee employee2 = new Sales() { Name = "Candy", Bonus = 1000 };
         Employee employee3 = new SalesLeader() { Name = "LuLu", Allowance = 5000 };

         var list = new List<Employee>() { employee1, employee2, employee3, null };

         foreach (var employee in list)
         {
             switch (employee)
             {
                 case SalesLeader l:
                     Console.WriteLine($"Employee Name {l.Name} , SalesLeader Allowance : {l.Allowance}");
                     break;
                 case Sales s:
                     Console.WriteLine($"Employee Name {s.Name} , Sales Bonus : {s.Bonus}");
                     break;
                 case null:
                     Console.WriteLine($"null");
                     break;
                 default:
                     Console.WriteLine($"Employee Name {employee.Name}");
                     break;
             }

         }

     }
}
class Employee
{
     public string Name { get; set; }
}

class Sales : Employee
{
     public int Bonus { get; set; }
}

class SalesLeader : Sales
{
     public int Allowance { get; set; }
}

 

SalesLeader類別繼承自Sales類別,而Sales類別繼承自Employee類別,範例中將SalesLeader、Sales與Employee物件放到集合之中,使用迴圈將集合中每一個物件取出,然後利用switch比對,若集合中的物件是SalesLeader類型的物件則印出Allowance;若集合中的物件是Sales類型則印出Bonus,此範例的執行結果,請參考下圖所示:

clip_image004

圖 2:型別比對。

進行型別比對(Type)時,case關鍵字後的運算式也可以搭配使用when關鍵字,例如將上例改寫如下,可以得到相同的執行結果:

 

class PatternMatchingDemo
{
     static void Main(string[] args)
     {
         Employee employee1 = new Employee() { Name = "Mary" };
         Employee employee2 = new Sales() { Name = "Candy", Bonus = 1000 };
         Employee employee3 = new SalesLeader() { Name = "LuLu", Allowance = 5000 };

         var list = new List<Employee>() { employee1, employee2, employee3, null };

         foreach (var employee in list)
         {
             switch (employee)
             {
                 case SalesLeader l:
                     Console.WriteLine($"Employee Name {l.Name} , SalesLeader Allowance : {l.Allowance}");
                     break;
                 case Sales s:
                     Console.WriteLine($"Employee Name {s.Name} , Sales Bonus : {s.Bonus}");
                     break;
                 case null:
                     Console.WriteLine($"null");
                     break;
                 default:
                     Console.WriteLine($"Employee Name {employee.Name}");
                     break;
             }

         }

     }
}
class Employee
{
     public string Name { get; set; }
}

class Sales : Employee
{
     public int Bonus { get; set; }
}

class SalesLeader : Sales
{
     public int Allowance { 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