前言 自從 2017 年 C# 7.0 版本開始引入聲明模式和常數(shù)模式匹配開始,到 2022 年的 C# 11 為止,最后一個(gè)板塊列表模式和切片模式匹配也已經(jīng)補(bǔ)齊,當(dāng)初計(jì)劃的模式匹配內(nèi)容已經(jīng)基本全部完成。 C# 在模式匹配方面下一步計(jì)劃則是支持活動(dòng)模式(active pattern),這一部分將在本文最后進(jìn)行介紹,而在介紹未來的模式匹配計(jì)劃之前,本文主題是對(duì)截止 C# 11 模式匹配的~~(不)~~完全指南,希望能對(duì)各位開發(fā)者們提升代碼編寫效率、可讀性和質(zhì)量有所幫助。 模式匹配 要使用模式匹配,首先要了解什么是模式。在使用正則表達(dá)式匹配字符串時(shí),正則表達(dá)式自己就是一個(gè)模式,而對(duì)字符串使用這段正則表達(dá)式進(jìn)行匹配的過程就是模式匹配。而在代碼中也是同樣的,我們對(duì)對(duì)象采用某種模式進(jìn)行匹配的過程就是模式匹配。
C# 11 支持的模式有很多,包含:
- 聲明模式(declaration pattern)
- 類型模式(type pattern)
- 常數(shù)模式(constant pattern)
- 關(guān)系模式(relational pattern)
- 邏輯模式(logical pattern)
- 屬性模式(property pattern)
- 位置模式(positional pattern)
- var 模式(var pattern)
- 丟棄模式(discard pattern)
- 列表模式(list pattern)
- 切片模式(slice pattern)
而其中,不少模式都支持遞歸,也就意味著可以模式嵌套模式,以此來實(shí)現(xiàn)更加強(qiáng)大的匹配功能。
模式匹配可以通過switch
表達(dá)式來使用,也可以在普通的switch
語句中作為case
使用,還可以在if
條件中通過is
來使用。本文主要在switch
表達(dá)式中使用模式匹配。
那么接下來就對(duì)這些模式進(jìn)行介紹。
實(shí)例:表達(dá)式計(jì)算器
為了更直觀地介紹模式匹配,我們接下來利用模式匹配來編寫一個(gè)表達(dá)式計(jì)算器。
為了編寫表達(dá)式計(jì)算器,首先我們需要對(duì)表達(dá)式進(jìn)行抽象:
publicabstractpartialclassExpr<T>whereT:IBinaryNumber<T>
{
publicabstractTEval(params(stringName,TValue)[]args);
}
我們用上面這個(gè)Expr
來表示一個(gè)表達(dá)式,其中T
是操作數(shù)的類型,然后進(jìn)一步將表達(dá)式分為常數(shù)表達(dá)式ConstantExpr
、參數(shù)表達(dá)式ParameterExpr
、一元表達(dá)式UnaryExpr
、二元表達(dá)式BinaryExpr
和三元表達(dá)式TernaryExpr
。最后提供一個(gè)Eval
方法,用來計(jì)算表達(dá)式的值,該方法可以傳入一個(gè)args
來提供表達(dá)式計(jì)算所需要的參數(shù)。
有了一、二元表達(dá)式自然也需要運(yùn)算符,例如加減乘除等,我們也同時(shí)定義Operator
來表示運(yùn)算符:
publicabstractrecordOperator
{
publicrecordUnaryOperator(OperatorsOperator):Operator;
publicrecordBinaryOperator(BinaryOperatorsOperator):Operator;
}
然后設(shè)置允許的運(yùn)算符,其中前三個(gè)是一元運(yùn)算符,后面的是二元運(yùn)算符:
publicenumOperators
{
[Description("~")]Inv,[Description("-")]Min,[Description("!")]LogicalNot,
[Description("+")]Add,[Description("-")]Sub,[Description("*")]Mul,[Description("/")]Div,
[Description("&")]And,[Description("|")]Or,[Description("^")]Xor,
[Description("==")]Eq,[Description("!=")]Ne,
[Description(">")]Gt,[Description("<")]Lt,[Description(">=")]Ge,[Description("<=")]Le,
[Description("&&")]LogicalAnd,[Description("||")]LogicalOr,
}
你可以能會(huì)好奇對(duì)T
的運(yùn)算能如何實(shí)現(xiàn)邏輯與或非,關(guān)于這一點(diǎn),我們直接使用0
來代表false
,非0
代表true
。
接下來就是分別實(shí)現(xiàn)各類表達(dá)式的時(shí)間!
常數(shù)表達(dá)式
常數(shù)表達(dá)式很簡(jiǎn)單,它保存一個(gè)常數(shù)值,因此只需要在構(gòu)造方法中將用戶提供的值存儲(chǔ)下來。它的Eval
實(shí)現(xiàn)也只需要簡(jiǎn)單返回存儲(chǔ)的值即可:
publicabstractpartialclassExpr<T>whereT:IBinaryNumber<T>
{
publicclassConstantExpr:Expr<T>
{
publicConstantExpr(Tvalue)=>Value=value;
publicTValue{get;}
publicvoidDeconstruct(outTvalue)=>value=Value;
publicoverrideTEval(params(stringName,TValue)[]args)=>Value;
}
}
參數(shù)表達(dá)式
參數(shù)表達(dá)式用來定義表達(dá)式計(jì)算過程中的參數(shù),允許用戶在對(duì)表達(dá)式執(zhí)行Eval
計(jì)算結(jié)果的時(shí)候傳參,因此只需要存儲(chǔ)參數(shù)名。它的Eval
實(shí)現(xiàn)需要根據(jù)參數(shù)名在args
中找出對(duì)應(yīng)的參數(shù)值:
publicabstractpartialclassExpr<T>whereT:IBinaryNumber<T>
{
publicclassParameterExpr:Expr<T>
{
publicParameterExpr(stringname)=>Name=name;
publicstringName{get;}
publicvoidDeconstruct(outstringname)=>name=Name;
//對(duì)args進(jìn)行模式匹配
publicoverrideTEval(params(stringName,TValue)[]args)=>argsswitch
{
//如果args有至少一個(gè)元素,那我們把第一個(gè)元素拿出來存為(name,value),
//然后判斷 name 是否和本參數(shù)表達(dá)式中存儲(chǔ)的參數(shù)名 Name 相同。
//如果相同則返回 value,否則用 args 除去第一個(gè)元素剩下的參數(shù)繼續(xù)匹配。
[var(name,value),..vartail]=>name==Name?value:Eval(tail),
//如果args是空列表,則說明在args中沒有找到名字和Name相同的參數(shù),拋出異常
[]=>thrownewInvalidOperationException($"Expectedanargumentnamed{Name}.")
};
}
}
模式匹配會(huì)從上往下依次進(jìn)行匹配,直到匹配成功為止。
上面的代碼中你可能會(huì)好奇[var (name, value), .. var tail]
是個(gè)什么模式,這個(gè)模式整體看是列表模式,并且列表模式內(nèi)組合使用聲明模式、位置模式和切片模式。例如:
-
[]
:匹配一個(gè)空列表。 -
[1, _, 3]
:匹配一個(gè)長度是 3,并且首尾元素分別是 1、3 的列表。其中_
是丟棄模式,表示任意元素。 -
[_, .., 3]
:匹配一個(gè)末元素是 3,并且 3 不是首元素的列表。其中..
是切片模式,表示任意切片。 -
[1, ..var tail]
:匹配一個(gè)首元素是 1 的列表,并且將除了首元素之外元素的切片賦值給tail
。其中var tail
是var
模式,用于將匹配結(jié)果賦值給變量。 -
[var head, ..var tail]
:匹配一個(gè)列表,將它第一個(gè)元素賦值給head
,剩下元素的切片賦值給tail
,這個(gè)切片里可以沒有元素。 -
[var (name, value), ..var tail]
:匹配一個(gè)列表,將它第一個(gè)元素賦值給(name, value)
,剩下元素的切片賦值給tail
,這個(gè)切片里可以沒有元素。其中(name, value)
是位置模式,用于將第一個(gè)元素的解構(gòu)結(jié)果根據(jù)位置分別賦值給name
和value
,也可以寫成(var name, var value)
。
一元表達(dá)式
一元表達(dá)式用來處理只有一個(gè)操作數(shù)的計(jì)算,例如非、取反等。
publicabstractpartialclassExpr<T>whereT:IBinaryNumber<T>
{
publicclassUnaryExpr:Expr<T>
{
publicUnaryExpr(UnaryOperatorop,Exprexpr )=>(Op,Expr)=(op,expr);
publicUnaryOperatorOp{get;}
publicExprExpr{get;}
publicvoidDeconstruct(outUnaryOperatorop,outExprexpr )=>(op,expr)=(Op,Expr);
//對(duì)Op進(jìn)行模式匹配
publicoverrideTEval(params(stringName,TValue)[]args)=>Opswitch
{
//如果Op是UnaryOperator,則將其解構(gòu)結(jié)果賦值給op,然后對(duì)op進(jìn)行匹配,op是一個(gè)枚舉,而.NET中的枚舉值都是整數(shù)
UnaryOperator(varop)=>opswitch
{
//如果op是Operators.Inv
Operators.Inv=>~Expr.Eval(args),
//如果op是Operators.Min
Operators.Min=>-Expr.Eval(args),
//如果op是Operators.LogicalNot
Operators.LogicalNot=>Expr.Eval(args)==T.Zero?T.One:T.Zero,
//如果op的值大于LogicalNot或者小于0,表示不是一元運(yùn)算符
>Operators.LogicalNotor0=>thrownewInvalidOperationException($"Expectedanunaryoperator,butgot{op}.")
},
//如果Op不是UnaryOperator
_=>thrownewInvalidOperationException("Expectedanunaryoperator.")
};
}
}
上面的代碼中,首先利用了 C# 元組可作為左值的特性,分別使用一行代碼就做完了構(gòu)造方法和解構(gòu)方法的賦值:(Op, Expr) = (op, expr)
和(op, expr) = (Op, Expr)
。如果你好奇能否利用這個(gè)特性交換多個(gè)變量,答案是可以!
在Eval
中,首先將類型模式、位置模式和聲明模式組合成UnaryOperator(var op)
,表示匹配UnaryOperator
類型、并且能解構(gòu)出一個(gè)元素的東西,如果匹配則將解構(gòu)出來的那個(gè)元素賦值給op
。
然后我們接著對(duì)解構(gòu)出來的op
進(jìn)行匹配,這里用到了常數(shù)模式,例如Operators.Inv
用來匹配op
是否是Operators.Inv
。常數(shù)模式可以使用各種常數(shù)對(duì)對(duì)象進(jìn)行匹配。
這里的> Operators.LogicalNot
和< 0
則是關(guān)系模式,分別用于匹配大于Operators.LogicalNot
的值和小于0
的指。然后利用邏輯模式or
將兩個(gè)模式組合起來表示或的關(guān)系。邏輯模式除了or
之外還有and
和not
。
由于我們?cè)谏厦娓F舉了枚舉中所有的一元運(yùn)算符,因此也可以將> Operators.LogicalNot or < 0
換成丟棄模式_
或者 var 模式var foo
,兩者都用來匹配任意的東西,只不過前者匹配到后直接丟棄,而后者聲明了個(gè)變量foo
將匹配到的值放到里面:
opswitch
{
//...
_=>thrownewInvalidOperationException($"Expectedanunaryoperator,butgot{op}.")
}
或
opswitch
{
//...
varfoo=>thrownewInvalidOperationException($"Expectedanunaryoperator,butgot{foo}.")
}
二元表達(dá)式
二元表達(dá)式用來表示操作數(shù)有兩個(gè)的表達(dá)式。有了一元表達(dá)式的編寫經(jīng)驗(yàn),二元表達(dá)式如法炮制即可。
publicabstractpartialclassExpr<T>whereT:IBinaryNumber<T>
{
publicclassBinaryExpr:Expr<T>
{
publicBinaryExpr(BinaryOperatorop,Exprleft,Exprright )=>(Op,Left,Right)=(op,left,right);
publicBinaryOperatorOp{get;}
publicExprLeft{get;}
publicExprRight{get;}
publicvoidDeconstruct(outBinaryOperatorop,outExprleft,outExprright )=>(op,left,right)=(Op,Left,Right);
publicoverrideTEval(params(stringName,TValue)[]args)=>Opswitch
{
BinaryOperator(varop)=>opswitch
{
Operators.Add=>Left.Eval(args)+Right.Eval(args),
Operators.Sub=>Left.Eval(args)-Right.Eval(args),
Operators.Mul=>Left.Eval(args)*Right.Eval(args),
Operators.Div=>Left.Eval(args)/Right.Eval(args),
Operators.And=>Left.Eval(args)&Right.Eval(args),
Operators.Or=>Left.Eval(args)|Right.Eval(args),
Operators.Xor=>Left.Eval(args)^Right.Eval(args),
Operators.Eq=>Left.Eval(args)==Right.Eval(args)?T.One:T.Zero,
Operators.Ne=>Left.Eval(args)!=Right.Eval(args)?T.One:T.Zero,
Operators.Gt=>Left.Eval(args)>Right.Eval(args)?T.One:T.Zero,
Operators.Lt=>Left.Eval(args)Left.Eval(args)>=Right.Eval(args)?T.One:T.Zero,
Operators.Le=>Left.Eval(args)<=?Right.Eval(args)???T.One?:?T.Zero,
????????????????Operators.LogicalAnd?=>Left.Eval(args)==T.Zero||Right.Eval(args)==T.Zero?T.Zero:T.One,
Operators.LogicalOr=>Left.Eval(args)==T.Zero&&Right.Eval(args)==T.Zero?T.Zero:T.One,
Operators.LogicalOr=>thrownewInvalidOperationException($"Unexpectedabinaryoperator,butgot{op}.")
},
_=>thrownewInvalidOperationException("Unexpectedabinaryoperator.")
};
}
}
同理,也可以將< Operators.Add or > Operators.LogicalOr
換成丟棄模式或者 var 模式。
三元表達(dá)式
三元表達(dá)式包含三個(gè)操作數(shù):條件表達(dá)式Cond
、為真的表達(dá)式Left
、為假的表達(dá)式Right
。該表達(dá)式中會(huì)根據(jù)Cond
是否為真來選擇取Left
還是Right
,實(shí)現(xiàn)起來較為簡(jiǎn)單:
publicabstractpartialclassExpr<T>whereT:IBinaryNumber<T>
{
publicclassTernaryExpr:Expr<T>
{
publicTernaryExpr(Exprcond,Exprleft,Exprright )=>(Cond,Left,Right)=(cond,left,right);
publicExprCond{get;}
publicExprLeft{get;}
publicExprRight{get;}
publicvoidDeconstruct(outExprcond,outExprleft,outExprright )=>(cond,left,right)=(Cond,Left,Right);
publicoverrideTEval(params(stringName,TValue)[]args)=>Cond.Eval(args)==T.Zero?Right.Eval(args):Left.Eval(args);
}
}
完成。我們用了僅僅幾十行代碼就完成了全部的核心邏輯!這便是模式匹配的強(qiáng)大之處:簡(jiǎn)潔、直觀且高效。
表達(dá)式判等
至此為止,我們已經(jīng)完成了所有的表達(dá)式構(gòu)造、解構(gòu)和計(jì)算的實(shí)現(xiàn)。接下來我們?yōu)槊恳粋€(gè)表達(dá)式實(shí)現(xiàn)判等邏輯,即判斷兩個(gè)表達(dá)式(字面上)是否相同。
例如a == b ? 2 : 4
和a == b ? 2 : 5
不相同,a == b ? 2 : 4
和c == d ? 2 : 4
不相同,而a == b ? 2 : 4
和a == b ? 2 : 4
相同。
為了實(shí)現(xiàn)該功能,我們重寫每一個(gè)表達(dá)式的Equals
和GetHashCode
方法。
常數(shù)表達(dá)式
常數(shù)表達(dá)式判等只需要判斷常數(shù)值是否相等即可:
publicoverrideboolEquals(object?obj)=>objisConstantExpr(varvalue)&&value==Value;
publicoverrideintGetHashCode()=>Value.GetHashCode();
參數(shù)表達(dá)式
參數(shù)表達(dá)式判等只需要判斷參數(shù)名是否相等即可:
publicoverrideboolEquals(object?obj)=>objisParameterExpr(varname)&&name==Name;
publicoverrideintGetHashCode()=>Name.GetHashCode();
一元表達(dá)式
一元表達(dá)式判等,需要判斷被比較的表達(dá)式是否是一元表達(dá)式,如果也是的話則判斷運(yùn)算符和操作數(shù)是否相等:
publicoverrideboolEquals(object?obj)=>objisUnaryExpr({Operator:varop},varexpr)&&(op,expr).Equals((Op.Operator,Expr));
publicoverrideintGetHashCode()=>(Op,Expr).GetHashCode();
上面的代碼中用到了屬性模式{ Operator: var op }
,用來匹配屬性的值,這里直接組合了聲明模式將屬性Operator
的值賦值給了expr
。另外,C# 中的元組可以組合起來進(jìn)行判等操作,因此不需要寫op.Equals(Op.Operator) && expr.Equals(Expr)
,而是可以直接寫(op, expr).Equals((Op.Operator, Expr))
。
二元表達(dá)式
和一元表達(dá)式差不多,區(qū)別在于這次多了一個(gè)操作數(shù):
publicoverrideboolEquals(object?obj)=>objisBinaryExpr({Operator:varop},varleft,varright)&&(op,left,right).Equals((Op.Operator,Left,Right));
publicoverrideintGetHashCode()=>(Op,Left,Right).GetHashCode();
三元表達(dá)式
和二元表達(dá)式差不多,只不過運(yùn)算符Op
變成了操作數(shù)Cond
:
publicoverrideboolEquals(object?obj)=>objisTernaryExpr(varcond,varleft,varright)&&cond.Equals(Cond)&&left.Equals(Left)&&right.Equals(Right);
publicoverrideintGetHashCode()=>(Cond,Left,Right).GetHashCode();
到此為止,我們?yōu)樗械谋磉_(dá)式都實(shí)現(xiàn)了判等。
一些工具方法
我們重載一些Expr
的運(yùn)算符方便我們使用:
publicstaticExproperator~(Exproperand)=>newUnaryExpr(new(Operators.Inv),operand);
publicstaticExproperator!(Exproperand)=>newUnaryExpr(new(Operators.LogicalNot),operand);
publicstaticExproperator-(Exproperand)=>newUnaryExpr(new(Operators.Min),operand);
publicstaticExproperator+(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Add),left,right);
publicstaticExproperator-(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Sub),left,right);
publicstaticExproperator*(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Mul),left,right);
publicstaticExproperator/(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Div),left,right);
publicstaticExproperator&(Exprleft,Exprright)=>newBinaryExpr(new(Operators.And),left,right);
publicstaticExproperator|(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Or),left,right);
publicstaticExproperator^(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Xor),left,right);
publicstaticExproperator>(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Gt),left,right);
publicstaticExproperator<(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Lt),left,right);
publicstaticExproperator>=(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Ge),left,right);
publicstaticExproperator<=(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Le),left,right);
publicstaticExproperator==(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Eq),left,right);
publicstaticExproperator!=(Exprleft,Exprright)=>newBinaryExpr(new(Operators.Ne),left,right);
publicstaticimplicitoperatorExpr(Tvalue)=>newConstantExpr(value);
publicstaticimplicitoperatorExpr(stringname)=>newParameterExpr(name);
publicstaticimplicitoperatorExpr(boolvalue)=>newConstantExpr(value?T.One:T.Zero);
publicoverrideboolEquals(object?obj)=>base.Equals(obj);
publicoverrideintGetHashCode()=>base.GetHashCode();
由于重載了==
和!=
,編譯器為了保險(xiǎn)起見提示我們重寫Equals
和GetHashCode
,這里實(shí)際上并不需要重寫,因此直接調(diào)用base
上的方法保持默認(rèn)行為即可。
然后編寫兩個(gè)擴(kuò)展方法用來方便構(gòu)造三元表達(dá)式,和從Description
中獲取運(yùn)算符的名字:
publicstaticclassExtensions
{
publicstaticExprSwitch(thisExprcond,Exprleft,Exprright)whereT:IBinaryNumber=>newExpr.TernaryExpr(cond,left,right);
publicstaticstring?GetName(thisTop)whereT:Enum=>typeof(T).GetMember(op.ToString()).FirstOrDefault()?.GetCustomAttribute()?.Description;
}
由于有參數(shù)表達(dá)式參與時(shí)需要我們提前提供參數(shù)值才能調(diào)用Eval
進(jìn)行計(jì)算,因此我們寫一個(gè)交互式的Eval
來在計(jì)算過程中遇到參數(shù)表達(dá)式時(shí)提示用戶輸入值,起名叫做InteractiveEval
:
publicTInteractiveEval()
{
varnames=Array.Empty<string>();
returnEval(GetArgs(this,refnames,refnames));
}
privatestaticTGetArg(stringname,refstring[]names)
{
Console.Write($"Parameter{name}:");
string?str;
do{str=Console.ReadLine();}
while(strisnull);
names=names.Append(name).ToArray();
returnT.Parse(str,NumberStyles.Number,null);
}
privatestatic(stringName,TValue)[]GetArgs(Exprexpr,refstring[]assigned,refstring[]filter )=>exprswitch
{
TernaryExpr(varcond,varleft,varright)=>GetArgs(cond,refassigned,refassigned).Concat(GetArgs(left,refassigned,refassigned)).Concat(GetArgs(right,refassigned,refassigned)).ToArray(),
BinaryExpr(_,varleft,varright)=>GetArgs(left,refassigned,refassigned).Concat(GetArgs(right,refassigned,refassigned)).ToArray(),
UnaryExpr(_,varuexpr)=>GetArgs(uexpr,refassigned,refassigned),
ParameterExpr(varname)=>filterswitch
{
[varhead,..]whenhead==name=>Array.Empty<(stringName,TValue)>(),
[_,..vartail]=>GetArgs(expr,refassigned,reftail),
[]=>new[]{(name,GetArg(name,refassigned))}
},
_=>Array.Empty<(stringName,TValue)>()
};
這里在GetArgs
方法中,模式[var head, ..]
后面跟了一個(gè)when head == name
,這里的when
用來給模式匹配指定額外的條件,僅當(dāng)條件滿足時(shí)才匹配成功,因此[var head, ..] when head == name
的含義是,匹配至少含有一個(gè)元素的列表,并且將頭元素賦值給head
,且僅當(dāng)head == name
時(shí)匹配才算成功。
最后我們?cè)僦貙?/span>ToString
方法方便輸出表達(dá)式,就全部大功告成了。
測(cè)試
接下來讓我測(cè)試測(cè)試我們編寫的表達(dá)式計(jì)算器:
Expr<int>a=4;
Expr<int>b=-3;
Expr<int>x="x";
Expr<int>c=!((a+b)*(a-b)>x);
Expr<int>y="y";
Expr<int>z="z";
Expr<int>expr=(c.Switch(y,z)-a>x).Switch(z+a,y/b);
Console.WriteLine(expr);
Console.WriteLine(expr.InteractiveEval());
運(yùn)行后得到輸出:
((((! ((((4) + (-3)) * ((4) - (-3))) > (x))) ? (y) : (z)) - (4)) > (x)) ? ((z) + (4)) : ((y) / (-3))
然后我們給x
、y
和z
分別設(shè)置成 42、27 和 35,即可得到運(yùn)算結(jié)果:
Parameterx:42
Parametery:27
Parameterz:35
-9
再測(cè)測(cè)表達(dá)式判等邏輯:
Expr<int>expr1,expr2,expr3;
{
Expr<int>a=4;
Expr<int>b=-3;
Expr<int>x="x";
Expr<int>c=!((a+b)*(a-b)>x);
Expr<int>y="y";
Expr<int>z="z";
expr1=(c.Switch(y,z)-a>x).Switch(z+a,y/b);
}
{
Expr<int>a=4;
Expr<int>b=-3;
Expr<int>x="x";
Expr<int>c=!((a+b)*(a-b)>x);
Expr<int>y="y";
Expr<int>z="z";
expr2=(c.Switch(y,z)-a>x).Switch(z+a,y/b);
}
{
Expr<int>a=4;
Expr<int>b=-3;
Expr<int>x="x";
Expr<int>c=!((a+b)*(a-b)>x);
Expr<int>y="y";
Expr<int>w="w";
expr3=(c.Switch(y,w)-a>x).Switch(w+a,y/b);
}
Console.WriteLine(expr1.Equals(expr2));
Console.WriteLine(expr1.Equals(expr3));
得到輸出:
True
False
活動(dòng)模式
在未來,C# 將會(huì)引入活動(dòng)模式,該模式允許用戶自定義模式匹配的方法,例如:
staticboolEven(thisTvalue)whereT:IBinaryInteger=>value%2==0;
上述代碼定義了一個(gè)T
的擴(kuò)展方法Even
,用來匹配value
是否為偶數(shù),于是我們便可以這么使用:
varx=3;
vary=xswitch
{
Even()=>"even",
_=>"odd"
};
此外,該模式還可以和解構(gòu)模式結(jié)合,允許用戶自定義解構(gòu)行為,例如:
staticboolInt(thisstringvalue,outintresult)=>int.TryParse(value,outresult);
然后使用的時(shí)候:
varx="3";
vary=xswitch
{
Int(varresult)=>result,
_=>0
};
即可對(duì)x
這個(gè)字符串進(jìn)行匹配,如果x
可以被解析為int
,就取解析結(jié)果result
,否則取 0。
總結(jié)
模式匹配極大的方便了我們編寫出簡(jiǎn)潔且可讀性高的高質(zhì)量代碼,并且會(huì)自動(dòng)幫我們做窮舉檢查,防止我們漏掉情況。此外,使用模式匹配時(shí),編譯器也會(huì)幫我們優(yōu)化代碼,減少完成匹配所需要的比較次數(shù),最終減少分支并提升運(yùn)行效率。
本文中的例子為了覆蓋到全部的模式,不一定采用了最優(yōu)的寫法,這一點(diǎn)各位讀者們也請(qǐng)注意。
本文中的表達(dá)式計(jì)算器全部代碼可以前往
-
字符串
+關(guān)注
關(guān)注
1文章
585瀏覽量
20611 -
代碼
+關(guān)注
關(guān)注
30文章
4830瀏覽量
69110 -
模式
+關(guān)注
關(guān)注
0文章
65瀏覽量
13432
原文標(biāo)題:C# 模式匹配完全指南
文章出處:【微信號(hào):vision263com,微信公眾號(hào):新機(jī)器視覺】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論