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_patternvar_pattern可能会导致局部变量的声明。

每个模式窗体定义可应用于该模式的输入值的类型集。 如果T模式是模式可能匹配的类型之一T,则模式P适用于类型。 如果某个模式出现在程序中以匹配类型的T模式P输入值(§11.1),则为编译时错误(如果P不适用T)。

示例:以下示例生成编译时错误,因为编译时类型 vTextReader. 类型的 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
}

但是,由于编译时类型 vobject,因此以下代码不会生成编译时错误。 类型的 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.1e,如果simple_designation标识符_,则表示放弃(§9.2.9.1)和 e 的值不绑定到任何内容。 (虽然具有该名称 _ 的声明变量可能位于该点的作用域内,但在此上下文中看不到该命名变量。如果 simple_designation 是任何其他标识符,则会引入由给定标识符命名的给定类型的局部变量(§9.2.9)。 当模式与值匹配时,向该局部变量分配模式输入值的值。

模式输入值和给定类型的静态类型的某些组合被视为不兼容,并导致编译时错误。 如果存在标识转换、隐式或显式引用转换、装箱转换或从中取消装箱转换或TE打开类型(§8.4.3),则表示静态类型的E值与该类型T兼容。 TE 声明模式命名类型T适用于与该类型兼容的T每种类型EE

注意:在检查可能为结构或类类型的类型时,对开放类型的支持最有用,并且要避免装箱。 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 且TT2为或某些基类型或接口T,则Nullable<T>类型(或装箱T)的值与类型模式T2 id匹配。 例如,在代码片段中

int? x = 3;
if (x is int v) { /* code using v */ }

语句的条件iftrue运行时,变量v保存块内类型的int3end 示例

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 == vtrue的结果为 e 的值匹配;否则
  • 如果返回,则模式P与值 e object.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.1e,如果指定标识符_,则表示放弃(§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 的类型,或者某些模式匹配nullQ
  • P是具有类型的T声明模式,并且该类型T§11.4)的模式Q集是详尽的。

11.4 模式详尽

非正式情况下,如果对于该类型(非 null)的每个可能值,则一组模式对于类型而言都是详尽的,则集中的某些模式适用。 以下规则定义类型一组模式 何时详尽

如果满足以下任一条件,则一组模式Q对于类型T而言是详尽的:

  1. T 是整数或枚举类型,或其中一个的可为 null 版本,对于'不可为 null 的基础类型的每个可能值 T,某些 Q 模式将匹配该值;或
  2. 某些模式 Qvar 模式;或
  3. 某些模式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 示例