01、模式匹配概述
從C#7
開始支援的 模式匹配 語法(糖,挺甜),可非常靈活的對資料進行條件匹配和提取,經過多個版本的完善,已經非常強大了。
C# 支援多種模式,包括宣告、型別、常量、關係、屬性、列表、var 和棄元等,在is
、switch
語句、switch
表示式中使用,還可以使用布林邏輯關鍵字 and
、or
和 not
組合多個模式,極大的簡化了程式碼編寫,可讀性也還不錯。
標題 | 說明 | 示例/備註 |
---|---|---|
型別和宣告模式 | 如果型別相容,則申明並賦值變數 | if (age is int i) |
常量模式 | 檢查表示式值是否等於、不等於(not)常量值 | if(age is null || age is 0) |
關係模式>< |
使用關係運算子< 、> 、<= 或 >= 匹配 |
case 0 or <=6 |
邏輯模式 | 用not >and >or 連線多個模式表示式 |
case < 12 and ( >6 or 6) |
屬性模式{:} |
對例項的屬性、欄位進行模式匹配:{屬性/欄位:匹配模式} |
if (b is { Year: < 2000, Month: 1 or 11 }) |
位置模式(解構) | 基於解構賦值進行模式匹配:(解構引數) |
if(point is (_,>0,>0)) |
var 模式 | 用var 申明(捕獲)任意區域性變數 |
if(point is var p && p.X>0) |
棄元模式_ | 棄元模式 _ 來匹配任何(其他)表示式 |
表示不要的 |
列表模式[] | 對陣列(列表)進行匹配,在中括號[] 中匹配列表中的項 |
if(numbers is [_, 2, 3, ..]) |
📢 模式匹配基本都是語法糖,味道都不錯!C#在編譯時會輸出原本的基礎程式碼,可透過 https://sharplab.io/ 線上檢視編譯後的程式碼。
02、模式匹配
2.1、型別和宣告模式
檢查型別是否匹配,同時申明變數,如果型別相容則申明並賦值變數,該變數在後面程式碼作用域中有效。
object age = "123";
if (age is int i) //型別匹配+申明變數i
{
Console.WriteLine($"age1 = {i}");
}
switch (age)
{
case string: //型別匹配
Console.WriteLine($"type is string");
break;
case int iage: //型別匹配+申明變數iage
Console.WriteLine($"age2 = {iage}");
break;
}
//上面is語句編譯後的程式碼效果:
if (obj is int)
{
int value = (int)obj;
}
2.2、常量模式
檢查表示式值是否等於、不等於(not
)常量值,常量值包括字面量常量,也包括const
常量值。傳統的Switch
語句就是常量模式匹配。
object age = null;
if (age is not null && age is 100) //age is 100 等同於 age is int && (int)age==100
{
Console.WriteLine($"age1 = {age}");
}
var type = age switch{
1 or 2 or 3=>"嬰兒",
4 => "幼兒",
null or not 5 => "unknow",
_=>"",
};
2.3、關係模式><
用關係運算子來匹配表示式,就是對常量數值進行大小比較運算,使用關係運算子<
、>
、<=
或 >=
,多個表示式可用and
、or
連線,當然也支援括號。
object age = 6;
if (age is int n and >= 6)
{
Console.WriteLine("666");
}
switch (age)
{
case 0 or <=6:
Console.WriteLine("幼兒");
break;
case < 12 and ( >6 or 6):
Console.WriteLine("青少年");
break;
}
2.4、邏輯模式not
/and
/or
用 not
、and
和 or
模式連結符來建立邏輯模式,連線多個模式表示式。
- 優先順序順序:
not
>and
>or
。 - 推薦使用
(括號)
顯示控制優先順序,可讀性更好。
object age = 6;
if (age is int n and (not 6 or >5) )
{
Console.WriteLine("666");
}
2.5、屬性模式{:}
對例項的屬性、欄位進行模式匹配,可以巢狀其他模式匹配,非常的強大,屬性匹配用大括號來包裝{屬性/欄位:匹配模式}
。
- 多個屬性/欄位都匹配為
true
時,最終才會匹配成功。 - 可以結合型別申明模式使用。
- 可巢狀使用,會遞迴匹配。
DateTime birthday = new DateTime(1999, 11, 12);
if (birthday is { Year: < 2000, Month: 1 or 11 })
{
Console.WriteLine("年齡、星座不合適");
}
//巢狀使用
public record Point(int X, int Y);
public record Segment(Point Start, Point End);
static bool IsAnyEndOnXAxis(Segment segment) =>
segment is { Start: { Y: 0 } } or { End: { Y: 0 } };
static bool IsAnyEndOnXAxis(Segment segment) =>
segment is { Start.Y: 0 } or { End.Y: 0 };
2.6、位置模式(解構)
基於解構賦值進行模式匹配:
Tuple
、record 和 DictionaryEntry是內建支援解構的,關於解構賦值可參考相關內容。- 用括號
()
報裝,這也是 解構(Deconstruct)的語法形式。 - 可以巢狀其他模式匹配,如常量、關係、邏輯、屬性模式等。
void Main()
{
Point point = new Point("sam", 12, 13);
var len = point switch
{
//型別匹配、屬性模式、位置模式:Name屬性必須為string,且長度為0,X、Y值為0
(string { Length: <= 0 }, 0, 0) => 0,
(_, > 0, 0) => point.X, //X值大於0,Y值為0
(_, 0, > 0) => point.Y, //Y值大於0,X值為0
(_, 10 or > 10, 10 or > 10) p => p.X * p.Y,
_ => 0,
};
}
public record Point(string Name, int X, int Y);
2.7、var 模式
用var
申明(捕獲)任意區域性變數,把表示式的結果分配給var
臨時變數。算是型別模式的變種,將型別名替換成了var
。
void Main()
{
Point point = new Point("sam", 12, 13);
if(point is var p && p.X>0 && p.Y>0){ //is var
Console.WriteLine("OK");
}
var len = point switch
{
var (_,x,y) when x>0 && y>0 => true,// var
};
}
public record Point(string Name, int X, int Y);
2.8、棄元模式_
棄元模式(Discard Pattern),字面理解就是被遺棄、沒人要的。可以將棄元模式看做是一個佔位符,表示一個沒人用的變數,可匹配任意型別,用來簡化程式碼。語法是用下劃線“_
”表示。
常用場景:
- 1、解構時的佔位符。
- 2、在
Switch
中匹配任意其他模式,類似default
的作用。 - 3、在
out
引數中佔位,表示一個沒人用的out
引數。 - 4、獨立棄元,接收無用的表示式輸出。
var tuple = new Tuple<int, int>(3, 4);
var (x, _) = tuple; //1、只需要第一個引數,其他就用“_”來佔位
Console.WriteLine(x); //3
_= x switch
{
2 or <2 => "small",
int and <18=>"young",
_=>"other", //2、匹配其他模式,效果同default
};
int.TryParse("", out _); //3、不用的out變數,實際上是申明瞭變數的
async void Print(object arg)
{
_ = arg ?? throw new ArgumentException(); //4、接收無用的返回,效果同下
if (arg == null) throw new ArgumentException();
_ = Task.Run(()=>Console.WriteLine("task run")); //接收一個不用的返回
}
棄元模式_
是一個提供給編譯器用的符號,告訴編譯這個變數不用了,編譯器會根據情況進行最佳化處理。在對out
引數使用時,編譯器會自動建立變數,如下程式碼:
int.TryParse("",out _);
//實際編譯後的程式碼如下
int result;
int.TryParse("", out result);
📢需要注意的是 下劃線
_
是並不是一個關鍵字,也能當做引數名來使用,不要混用。
2.9、列表模式[]
C#11支援的,對陣列(列表)進行匹配,在中括號[]
中匹配列表中的項。
- 跳過的項可以用棄元模式
_
。 - 可以用陣列的切片模式匹配開頭、結尾的元素。
void Main()
{
int[] numbers = { 1, 2, 3, 4 };
Console.WriteLine(numbers is [_, 2, 3, ..]); // True
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]); // False
}
03、模式匹配應用
上面的各種模式匹配主要就用在 is 運算子、switch 語句、switch 表示式 中。
3.1、is運算子
is 運算子 本來主要是用來檢測型別相容性的,加上模式匹配就能玩出各種花樣了,極大簡化了讓各種檢查類的程式碼。
object value = 12;
if (value is int && value is not null) //is型別檢測+邏輯模式
{
Console.WriteLine(value);
}
if (value is int a && a > 6) //+申明模式
{
Console.WriteLine(a);
}
if (value is int age and > 10 and < 14) //關係模式
{
Console.WriteLine(age);
}
var user = new { Name = "sam", Age = 12 };
if (user is { Name: _, Age: > 10 }) //屬性模式
{
Console.WriteLine(user.Name);
}
int[] arr = new int[] { 1, 2, 3 };
if (arr is [> 0, ..]) //列表模式:第一個元素>0
{
Console.WriteLine(arr);
}
var dt = new Tuple<string, int>("sam", 100);
if (dt is (_, > 60) d) //位置模式+申明模式(好像沒什麼用)
{
Console.WriteLine(d.Item1);
}
3.2、switch..case語句
switch..case 語句 是很多語言中都有的基本多條件分支語句,傳統的 case 只能用於匹配常量,多用於列舉。
case
不能穿透,一個case
執行完後必須break
結束,或者return
返回(退出方法),可以多個case
匹配執行一組邏輯程式碼。- 傳統的
case
就是常量模式,而現代的case
可以結合上面多種模式使用,非常強大。 - 加
when
,自由附加更多條件。
int age = 22;
string sex = "Male";
switch (age)
{
case 1:
case 2:
Console.WriteLine("嬰兒");
break;
case <= 3:
Console.WriteLine("幼兒");
break;
case > 10 and < 16:
Console.WriteLine("青少年");
break;
case > 18 when sex == "Male":
Console.WriteLine("成年男性");
break;
case int:
break;
}
3.3、switch表示式
C#8
中switch
有了新的語法 —— switch 表示式 ,可以看做是switch..case
語句的一個變種,使用比較類似。switch
表示式是一個賦值(輸出)語句。
=>
左側為模式(返回一個bool),如果模式匹配(true)則返回右側的值,最後一個棄元模式匹配其他情況,同default
效果。
int type = 6;
var message = type switch
{
<= 1 => "success",
2 => "warning",
3 => "error",
> 3 and < 10 => "other error",
_ => "unkonwn error",
};
可以用when
來進行更多的判斷,when
後面的表示式就很自由了,只要返回boo
即可。
object type = 6;
var message = type switch
{
int i when i<6 => "ok",
string s when s=="null"=>"Null",
string s when !string.IsNullOrEmpty(s)=>"string value",
_=>"unknown value"
};
Console.WriteLine(message);
支援多個變數的組合模式:用括號()
包含多個變數
string gender = "male";
int age = 10;
string type = (gender,age) switch{
("male",>18)=>"VIP",
(not "male",>26 and <35)=>"VVIP",
_=>"",
};
參考資料
- 模式匹配 - 模式中的 is 和 switch 表示式,以及 and、or 和 not 運算子
- 析構元組和其他型別
©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀