11 模式和模式匹配
11.1 常规
模式是一种语法形式,可与运算符(§12.12.12.12)和switch_statement(§13.8.3)一起使用is
,以表达要对其比较传入数据的数据的形状。 针对 switch 语句的表达式或运算符左侧is
的relational_expression(每个运算符称为模式输入值)测试模式。
11.2 模式窗体
11.2.1 常规
模式可能具有以下形式之一:
pattern
: declaration_pattern
| constant_pattern
| var_pattern
;
declaration_pattern和var_pattern可能会导致局部变量的声明。
每个模式窗体定义可应用于该模式的输入值的类型集。 如果T
模式是模式可能匹配的类型之一T
,则模式P
适用于类型。 如果某个模式出现在程序中以匹配类型的T
模式P
输入值(§11.1),则为编译时错误(如果P
不适用T
)。
示例:以下示例生成编译时错误,因为编译时类型
v
为TextReader
. 类型的TextReader
变量永远不能具有与以下项兼容的string
值:TextReader v = Console.In; // compile-time type of 'v' is 'TextReader' if (v is string) // compile-time error { // code assuming v is a string }
但是,由于编译时类型
v
为object
,因此以下代码不会生成编译时错误。 类型的object
变量可以具有与引用兼容的string
值:object v = Console.In; if (v is string s) { // code assuming v is a string }
end 示例
每个模式窗体定义模式 在运行时匹配 值的一组值。
11.2.2 声明模式
declaration_pattern用于测试某个值是否具有给定类型,如果测试成功,请在该类型的变量中提供该值。
declaration_pattern
: type simple_designation
;
simple_designation
: single_variable_designation
;
single_variable_designation
: identifier
;
值的运行时类型使用 is-type 运算符中指定的相同规则(§12.12.12.12.1)针对模式中的类型进行测试。 如果测试成功,则模式 与该值匹配 。 如果 类型 为可以为 null 的值类型(§8.3.12),则为编译时错误。 此模式形式永远不会与值 null
匹配。
注意:is-type 表达式
e is T
和声明模式e is T _
在不是可以为 null 的类型时T
是等效的。 end note
给定模式输入值(§11.1)e,如果simple_designation是标识符_
,则表示放弃(§9.2.9.1)和 e 的值不绑定到任何内容。 (虽然具有该名称 _
的声明变量可能位于该点的作用域内,但在此上下文中看不到该命名变量。如果 simple_designation 是任何其他标识符,则会引入由给定标识符命名的给定类型的局部变量(§9.2.9)。 当模式与值匹配时,向该局部变量分配模式输入值的值。
模式输入值和给定类型的静态类型的某些组合被视为不兼容,并导致编译时错误。 如果存在标识转换、隐式或显式引用转换、装箱转换或从中取消装箱转换或T
E
打开类型(§8.4.3),则表示静态类型的E
值与该类型T
兼容。 T
E
声明模式命名类型T
适用于与该类型兼容的T
每种类型E
。E
注意:在检查可能为结构或类类型的类型时,对开放类型的支持最有用,并且要避免装箱。 end note
示例:声明模式可用于执行引用类型的运行时类型测试,并替换成语
var v = expr as Type; if (v != null) { /* code using v */ }
稍微简洁一点
if (expr is Type v) { /* code using v */ }
end 示例
如果 类型 为可为 null 的值类型,则为错误。
示例:声明模式可用于测试可为 null 类型的值:如果值不为 null 且
T
T2
为或某些基类型或接口T
,则Nullable<T>
类型(或装箱T
)的值与类型模式T2 id
匹配。 例如,在代码片段中int? x = 3; if (x is int v) { /* code using v */ }
语句的条件
if
在true
运行时,变量v
保存块内类型的int
值3
。 end 示例
11.2.3 常量模式
constant_pattern用于针对给定常量值测试模式输入值(§11.1)的值。
constant_pattern
: constant_expression
;
如果存在从常量表达式P
到该类型的T
隐式转换,则常量模式P
适用于类型T
。
对于常量模式 P
,其 转换的值 为
- 如果模式输入值的类型是整型类型或枚举类型,则模式的常量值转换为该类型;否则
- 如果模式输入值的类型是整型类型或枚举类型的可为 null 版本,则模式的常量值转换为其基础类型;否则
- 模式常量值的值。
给定模式输入值 e 和具有转换值 v 的常量模式P
,
- 如果 e 具有整型类型或枚举类型,或其中一种可为 null 的形式,并且 v 具有整型类型,则模式
P
与表达式e == v
true
的结果为 e 的值匹配;否则 - 如果返回,则模式
P
与值 eobject.Equals(e, v)
匹配。true
示例:
switch
以下方法中的语句在其事例标签中使用五个常量模式。static decimal GetGroupTicketPrice(int visitorCount) { switch (visitorCount) { case 1: return 12.0m; case 2: return 20.0m; case 3: return 27.0m; case 4: return 32.0m; case 0: return 0.0m; default: throw new ArgumentException(...); } }
end 示例
11.2.4 Var 模式
var_pattern 匹配每个值。 也就是说,具有 var_pattern 的模式匹配操作始终成功。
var_pattern适用于每种类型。
var_pattern
: 'var' designation
;
designation
: simple_designation
;
给定模式输入值(§11.1)e,如果指定为标识符_
,则表示放弃(§9.2.9.1),e 的值不绑定到任何内容。 (尽管具有该名称的声明变量可能位于该点的范围中,但在此上下文中看不到该命名变量。如果指定为任何其他标识符,则 e 的值将绑定到该名称的新引入的局部变量(§9.2.9),其类型为 e 的静态类型,并将模式输入值分配给该局部变量。
如果名称var
绑定到使用var_pattern的类型,则为错误。
11.3 模式子建议
在 switch 语句中,如果事例的模式被前面的未保护事例集(§13.8.3)子化,则为错误。 非正式地说,这意味着任何输入值都将与前面的案例之一匹配。 以下规则定义一组模式何时将给定模式子化:
P
如果模式的运行时行为规范与该模式的运行时行为匹配P
,则模式将匹配K
常量K
。
如果满足以下任一条件,则一组模式Q
会子化模式P
:
P
是一个常量模式,集中Q
的任何模式都与转换后的值匹配P
P
是 var 模式,模式集Q
对于模式输入值的类型(§11.4)是详尽的(§11.4),并且模式输入值不是可以为 null 的类型,或者某些模式匹配null
Q
。P
是具有类型的T
声明模式,并且该类型T
(§11.4)的模式Q
集是详尽的。
11.4 模式详尽
非正式情况下,如果对于该类型(非 null)的每个可能值,则一组模式对于类型而言都是详尽的,则集中的某些模式适用。 以下规则定义类型一组模式 何时详尽 :
如果满足以下任一条件,则一组模式Q
对于类型T
而言是详尽的:
T
是整数或枚举类型,或其中一个的可为 null 版本,对于'不可为 null 的基础类型的每个可能值T
,某些Q
模式将匹配该值;或- 某些模式
Q
是 var 模式;或 - 某些模式
Q
是类型的D
声明模式,并且存在标识转换、隐式引用转换或从T
中转换到D
的装箱转换。
示例:
static void M(byte b) { switch (b) { case 0: case 1: case 2: ... // handle every specific value of byte break; // error: the pattern 'byte other' is subsumed by the (exhaustive) // previous cases case byte other: break; } }
end 示例