Compartir a través de


11 Patrones y coincidencia de patrones

11.1 General

Un patrón es una forma sintáctica que se puede usar con el is operador (§12.12.12) y en una switch_statement (§13.8.3) para expresar la forma de datos con los que se van a comparar los datos entrantes. Un patrón se prueba con la expresión de una instrucción switch o con una relational_expression que se encuentra en el lado izquierdo de un is operador, cada uno de los cuales se conoce como un valor de entrada de patrón.

11.2 Formularios de patrón

11.2.1 General

Un patrón puede tener una de las formas siguientes:

pattern
    : declaration_pattern
    | constant_pattern
    | var_pattern
    ;

Un declaration_pattern y un var_pattern pueden dar lugar a la declaración de una variable local.

Cada formulario de patrón define el conjunto de tipos para los valores de entrada a los que se puede aplicar el patrón. Un patrón P es aplicable a un tipo T si T está entre los tipos cuyos valores puede coincidir el patrón. Es un error en tiempo de compilación si un patrón P aparece en un programa para que coincida con un valor de entrada de patrón (§11.1) de tipo T si P no es aplicable a T.

Ejemplo: el ejemplo siguiente genera un error en tiempo de compilación porque el tipo de tiempo de compilación de v es TextReader. Una variable de tipo TextReader nunca puede tener un valor compatible con 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
}

Sin embargo, lo siguiente no genera un error en tiempo de compilación porque el tipo de tiempo de compilación de v es object. Una variable de tipo object podría tener un valor compatible con la referencia con string:

object v = Console.In;
if (v is string s)
{
    // code assuming v is a string
}

ejemplo final

Cada formulario de patrón define el conjunto de valores para los que el patrón coincide con el valor en tiempo de ejecución.

11.2.2 Patrón de declaración

Un declaration_pattern se usa para probar que un valor tiene un tipo determinado y, si la prueba se realiza correctamente, proporcione el valor en una variable de ese tipo.

declaration_pattern
    : type simple_designation
    ;
simple_designation
    : single_variable_designation
    ;
single_variable_designation
    : identifier
    ;

El tipo en tiempo de ejecución del valor se prueba con el tipo del patrón mediante las mismas reglas especificadas en el operador is-type (§12.12.12.12.1). Si la prueba se realiza correctamente, el patrón coincide con ese valor. Es un error en tiempo de compilación si el tipo es un tipo de valor que acepta valores NULL (§8.3.12). Este formulario de patrón nunca coincide con un null valor.

Nota: La expresión e is T is-type y el patrón e is T _ de declaración son equivalentes cuando T no es un tipo que acepta valores NULL. nota final

Dado un valor de entrada de patrón (§11.1) e, si el simple_designation es el identificador _ , denota un descarte (§9.2.9.1) y el valor de e no está enlazado a nada. (Aunque una variable declarada con el nombre _ puede estar en el ámbito en ese momento, esa variable con nombre no se ve en este contexto). Si simple_designation es cualquier otro identificador, se introduce una variable local (§9.2.9) del tipo especificado denominado por el identificador especificado. A esa variable local se le asigna el valor del valor de entrada del patrón cuando el patrón coincide con el valor.

Ciertas combinaciones de tipo estático del valor de entrada de patrón y el tipo especificado se consideran incompatibles y producen un error en tiempo de compilación. Se dice que un valor de tipo E estático es compatible con el patrón T si existe una conversión de identidad, una conversión de referencia implícita o explícita, una conversión de conversión boxing o una conversión unboxing de E a T, o si o E T es un tipo abierto (§8.4.3). Un patrón de declaración que asigna un nombre a un tipo T es aplicable a cada tipo E para el que E es compatible con Tel patrón .

Nota: La compatibilidad con tipos abiertos puede ser más útil al comprobar tipos que pueden ser tipos de estructura o clase, y se debe evitar la conversión boxing. nota final

Ejemplo: el patrón de declaración es útil para realizar pruebas de tipos en tiempo de ejecución de tipos de referencia y reemplaza la expresión

var v = expr as Type;
if (v != null) { /* code using v */ }

con un poco más conciso

if (expr is Type v) { /* code using v */ }

ejemplo final

Se trata de un error si el tipo es un tipo de valor que acepta valores NULL.

Ejemplo: El patrón de declaración se puede usar para probar valores de tipos que aceptan valores NULL: un valor de tipo Nullable<T> (o un boxed T) coincide con un patrón T2 id de tipo si el valor no es NULL y T2 es T, o algún tipo base o interfaz de T. Por ejemplo, en el fragmento de código

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

La condición de la if instrucción está true en tiempo de ejecución y la variable v contiene el valor 3 de tipo int dentro del bloque. ejemplo final

11.2.3 Patrón constante

Un constant_pattern se usa para probar el valor de un valor de entrada de patrón (§11.1) con el valor constante especificado.

constant_pattern
    : constant_expression
    ;

Un patrón P de constante se aplica a un tipo T si hay una conversión implícita de la expresión constante de P al tipo T.

Para un patrón Pconstante, su valor convertido es

  • si el tipo del valor de entrada del patrón es un tipo entero o un tipo de enumeración, el valor constante del patrón se convierte en ese tipo; de otra manera
  • si el tipo del valor de entrada del patrón es la versión que acepta valores NULL de un tipo entero o un tipo de enumeración, el valor constante del patrón se convierte en su tipo subyacente; de otra manera
  • el valor del valor constante del patrón.

Dado un valor de entrada de patrón e y un patrón P constante con valor convertido v,

  • si e tiene tipo entero o tipo de enumeración, o una forma que acepta valores NULL de uno de ellos, y v tiene un tipo entero, el patrón P coincide con el valor e si el resultado de la expresión e == v es true; de lo contrario,
  • el patrón P coincide con el valor e si object.Equals(e, v) devuelve true.

Ejemplo: la switch instrucción del método siguiente usa cinco patrones constantes en sus etiquetas de mayúsculas y minúsculas.

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(...);
    }
}

ejemplo final

Patrón var 11.2.4

Un var_pattern coincide con cada valor. Es decir, una operación de coincidencia de patrones con una var_pattern siempre se realiza correctamente.

Un var_pattern es aplicable a cada tipo.

var_pattern
    : 'var' designation
    ;
designation
    : simple_designation
    ;

Dado un valor de entrada de patrón (§11.1) e, si la designación es el identificador _ , denota un descarte (§9.2.9.1) y el valor de e no está enlazado a nada. (Aunque una variable declarada con ese nombre puede estar en el ámbito en ese momento, esa variable con nombre no se ve en este contexto). Si la designación es cualquier otro identificador, en tiempo de ejecución el valor de e está enlazado a una variable local recién introducida (§9.2.9) de ese nombre cuyo tipo es el tipo estático de e y el valor de entrada del patrón se asigna a esa variable local.

Se trata de un error si el nombre var se enlazaría a un tipo en el que se usa un var_pattern .

Subsumpción de patrón 11.3

En una instrucción switch, se trata de un error si el patrón de un caso está subsumado por el conjunto anterior de casos no supervisados (§13.8.3). Informalmente, esto significa que cualquiera de los valores de entrada habría sido coincidente con uno de los casos anteriores. Las reglas siguientes definen cuándo un conjunto de patrones subsume un patrón determinado:

Un patrón P coincidiría con una constante K si la especificación del comportamiento en tiempo de ejecución de ese patrón es que P coincide con K.

Un conjunto de patrones Q subsumes un patrón P si alguna de las condiciones siguientes contiene:

  • Pes un patrón constante y cualquiera de los patrones del conjunto Q coincidiría con Pel valor convertido de .
  • Pes un patrón var y el conjunto de patrones es exhaustivo (§11.4) para el tipo del valor de entrada del patrón (§11.1), y el valor de entrada del patrón no es de un tipo que acepta valores NULL o algún patrón de Q coincidiría nullcon . Q
  • P es un patrón de declaración con tipo T y el conjunto de patrones Q es exhaustivo para el tipo T (§11.4).

11.4 Exhaustiva de patrones

Informalmente, un conjunto de patrones es exhaustivo para un tipo si, para cada valor posible de ese tipo distinto de NULL, se aplica algún patrón del conjunto. Las reglas siguientes definen cuándo un conjunto de patrones es exhaustivo para un tipo:

Un conjunto de patrones Q es exhaustivo para un tipo T si alguna de las condiciones siguientes contiene:

  1. T es un tipo entero o de enumeración, o una versión que acepta valores NULL de uno de ellos, y para cada valor posible del Ttipo subyacente que no acepta valores NULL, algún patrón de Q coincidiría con ese valor; o
  2. Algún patrón de Q es un patrón var; o
  3. Algún patrón de Q es un patrón de declaración para el tipo Dy hay una conversión de identidad, una conversión de referencia implícita o una conversión boxing de T a D.

Ejemplo:

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;
    }
}

ejemplo final