La coincidencia de patrones es una característica que permite probar una expresión para la ocurrencia de un patrón dado. Es una característica más frecuente en los lenguajes funcionales . La coincidencia de patrones es de naturaleza booleana, lo que implica que hay dos resultados posibles: la expresión coincide con el patrón o no. Esta característica se introdujo por primera vez en C# 7.0 y luego ha sufrido una serie de mejoras en las sucesivas versiones del lenguaje.
La coincidencia de patrones permite operaciones como:
- Comprobación de tipo (patrón de tipo)
- verificación nula (patrón constante)
- comparaciones (patrón relacional)
- Comprobación y comparación de valores de propiedades (patrón de propiedad)
- deconstrucción de objetos (patrón posicional),
- reutilización de expresiones usando la creación de variables ( patrón var )
expresarse utilizando una sintaxis mínima y sucinta. Además, estos patrones se pueden anidar y pueden comprender varios subpatrones. Los patrones también se pueden combinar usando combinadores de patrones ( y , o y no ). C# permite la coincidencia de patrones a través de tres construcciones:
1. es operador
Antes de C# 7.0, el único propósito del operador is era verificar si un objeto es compatible con un tipo específico. Desde C# 7.0, el operador is se ha ampliado para probar si una expresión coincide con un patrón.
Sintaxis:
la expresión es patrón
2. cambiar declaraciones
Al igual que se puede usar una declaración de cambio para ejecutar una rama de código ( caso ) probando una expresión para un conjunto de valores, también se puede usar para ejecutar una rama de código probando una expresión para la ocurrencia de un conjunto de valores. patrones.
Sintaxis:
interruptor (expresión)
{
caso patrón1:
// código a ejecutar
// si la expresión coincide con el patrón1
descanso;
caso patrón2:
// código a ejecutar
// si la expresión coincide con el patrón2
descanso;
…
patrón de casoN:
// código a ejecutar
// si la expresión coincide con patrónN
descanso;
defecto:
// código a ejecutar si expresión
// no coincide con ninguno de los patrones anteriores
}
3. cambiar expresiones
También se puede probar un conjunto de patrones mediante una expresión de cambio para seleccionar un valor en función de si el patrón coincide.
Sintaxis:
cambio de expresión
{
patrón1 => valor1,
patrón2 => valor2,
…
patrónN => valorN,
_ => valor predeterminado
}
Patrones compatibles con C#
A partir de C# 9, el lenguaje admite los siguientes patrones.
- Tipo Patrón
- Patrón relacional
- Patrón de propiedad
- Patrón posicional
- patrón variable
- Patrón constante
C# también admite el uso de las siguientes construcciones con coincidencia de patrones:
- Declaraciones de variables
- Combinadores de patrones ( y , o y no )
- Descartar variables ( _ )
- Patrones anidados o subpatrones
El patrón tipográfico
El patrón de tipo se puede utilizar para comprobar si el tipo de tiempo de ejecución de una expresión coincide con el tipo especificado o es compatible con ese tipo. Si la expresión, es decir, el valor que se compara es compatible con el tipo especificado en el patrón, la coincidencia se realiza correctamente. El patrón de tipo también puede contener opcionalmente una declaración de variable. Si el valor que se está probando coincide con el tipo, se convertirá a este tipo y luego se asignará a esta variable. Las declaraciones de variables en patrones se describen más adelante.
Sintaxis:
// Usado en C# 9 y superior
Escribe un nombre
// Usado en C# 7
Variable de nombre de tipo
Escribe un nombre _
Ejemplo:
C#
// C# program to illustrate the concept of Type Pattern using System; public class GFG{ static void PrintUppercaseIfString(object arg) { // If arg is a string: // convert it to a string // and assign it to variable message if (arg is string message) { Console.WriteLine($"{message.ToUpper()}"); } else { Console.WriteLine($"{arg} is not a string"); } } // Driver code static public void Main() { string str = "Geeks For Geeks"; int number = 42; object o1 = str; object o2 = number; PrintUppercaseIfString(o1); PrintUppercaseIfString(o2); } }
GEEKS FOR GEEKS 42 is not a string
En el ejemplo anterior, el método PrintUppercaseIfString() acepta un argumento de tipo objeto llamado arg . Cualquier tipo en C# se puede convertir en objeto porque, en C#, todos los tipos se derivan del objeto. Esto se llama unificación de tipos .
Fundición automática
Si arg es una string , se convertirá de objeto a string y se asignará a una variable llamada mensaje . Si arg no es una string sino un tipo diferente, se ejecutará el bloque else . Por lo tanto, tanto la verificación de tipos como la conversión se combinan en una expresión. Si el tipo no coincide, la variable no se creará.
Activar tipos con el patrón de tipos
El patrón de tipo utilizado con una declaración de cambio puede ayudar a seleccionar una rama de código ( rama de caso ) según el tipo de valor. El siguiente código define un método llamado PrintType() que acepta un argumento como un objeto y luego imprime diferentes mensajes para diferentes tipos de argumentos:
C#
// C# program to illustrate the concept of Type Pattern Switch using static System.Console; // Allows using WriteLine without Console. prefix public class Person { public string Name { get; set; } } class GFG{ static void PrintType(object obj) { switch (obj) { case Person p: WriteLine("obj is a Person"); WriteLine($"Name of the person: {p.Name}"); break; case int i: WriteLine("obj is an int"); WriteLine($"Value of the int: {i}"); break; case double d: WriteLine("obj is a double"); WriteLine($"Value of the double: {d}"); break; default: WriteLine("obj is some other type"); break; } WriteLine(); // New line } // Driver code static public void Main() { var person = new Person { Name = "Geek" }; PrintType(42); PrintType(person); PrintType(3.14); PrintType("Hello"); } }
Producción:
obj is an int Value of the int: 42 obj is a Person Name of the person: Geek obj is a double Value of the double: 3.14 obj is some other type
Patrones relacionales
Los patrones relacionales se introdujeron en C# 9. Nos ayudan a realizar comparaciones en un valor mediante: <(menor que), <=(menor que o igual a), >(mayor que) y >=(mayor que o igual a ) operadores.
Sintaxis:
< constante
<= constante
> constante
>= constante
Ejemplo:
C#
// Program to check if a number is positive, // negative or zero using relational patterns // using a switch statement using System; class GFG{ public static string GetNumberSign(int number) { switch (number) { case < 0: return "Negative"; case 0: return "Zero"; case >= 1: return "Positive"; } } // Driver code static public void Main() { int n1 = 0; int n2 = -31; int n3 = 18; Console.WriteLine(GetNumberSign(n1)); Console.WriteLine(GetNumberSign(n2)); Console.WriteLine(GetNumberSign(n3)); } }
Producción:
Zero Negative Positive
El ejemplo anterior se puede escribir de manera más concisa usando una expresión de cambio :
C#
// Program to check if a number // is positive, negative or zero // using relational patterns // with a switch expression using System; class GFG{ public static string GetNumberSign(int number) { return number switch { < 0 => "Negative", 0 => "Zero", >= -1 => "Positive" }; } // Driver code static public void Main() { int n1 = 0; int n2 = -31; int n3 = 18; Console.WriteLine(GetNumberSign(n1)); Console.WriteLine(GetNumberSign(n2)); Console.WriteLine(GetNumberSign(n3)); } }
Producción:
Zero Negative Positive
De manera similar, los patrones relacionales también se pueden usar con el operador is :
int n = 2; Console.WriteLine(n is <= 10); // Prints true Console.WriteLine(n is > 5); // Prints false
Esto puede no ser tan útil por sí solo porque n es <= 10 es lo mismo que escribir n <= 10 . Sin embargo, esta sintaxis será más conveniente con los combinadores de patrones (discutidos más adelante).
Patrones de propiedad
Los patrones de propiedad permiten hacer coincidir los valores de las propiedades definidas en un objeto. El patrón especifica el nombre de la propiedad que debe coincidir y luego, después de dos puntos (:), el valor que debe coincidir. Se pueden especificar múltiples propiedades y sus valores separándolos con comas.
Sintaxis:
{ Propiedad1: valor1, Propiedad2 : valor2, …, PropiedadN: valorN }
Tal sintaxis nos permite escribir:
«Geeks» es { Duración: 4 }
En vez de:
“Geeks”. Longitud == 4
Ejemplo:
C#
// C# program to illustrate the concept of Property Pattern using System; class GFG{ public static void DescribeStringLength(string str) { // Constant pattern, discussed further if (str is null) { Console.WriteLine("Null string"); } if (str is { Length: 0 }) { Console.WriteLine("Empty string"); return; } if (str is { Length: 1 }) { Console.WriteLine("String of length 1"); return; } Console.WriteLine("Length greater than 1"); return; } // Driver code static public void Main() { DescribeStringLength("Hello!"); Console.WriteLine(); DescribeStringLength(""); Console.WriteLine(); DescribeStringLength("X"); Console.WriteLine(); } }
Producción:
Length greater than 1 Empty string String of length 1
Patrones posicionales
Los patrones posicionales permiten especificar un conjunto de valores entre paréntesis y coincidirán si cada valor entre paréntesis coincide con los valores del objeto coincidente. Los valores de los objetos se extraen a través de la deconstrucción . Los patrones posicionales se basan en el patrón de deconstrucción. Los siguientes tipos pueden usar patrones posicionales:
- Cualquier tipo con uno o más deconstructores . Se dice que un tipo tiene un deconstructor si define uno o más métodos Deconstruct() que aceptan uno o más parámetros de salida . El método Deconstruct() también se puede definir como un método de extensión .
- Tipos de tuplas (instancias de System.ValueTuple ).
- Tipos de registros posicionales. (desde C# 9).
Sintaxis:
(constante1, constante2, …)
Ejemplo 1: patrón posicional con un tipo que define un método Deconstruct()
El siguiente código define dos funciones LogicalAnd() y LogicalOr() , las cuales aceptan un objeto de BooleanInput. BooleanInput es un tipo de valor (estructura) que representa dos valores de entrada booleanos. Los métodos usan estos dos valores de entrada y realizan una operación Lógica AND y Lógica OR en estos valores. C# ya tiene operadores lógicos AND(&&) y lógicos OR(||) que realizan estas operaciones por nosotros. Sin embargo, los métodos de este ejemplo implementan estas operaciones manualmente para demostrar patrones posicionales.
C#
// C# program to illustrate the concept of Positional Pattern using System; // Represents two inputs to the truth table public struct BooleanInput { public bool Input1 { get; set; } public bool Input2 { get; set; } public void Deconstruct(out bool input1, out bool input2) { input1 = Input1; input2 = Input2; } } class GFG{ // Performs logical AND on an input object public static bool LogicalAnd(BooleanInput input) { // Using switch expression return input switch { (false, false) => false, (true, false) => false, (false, true) => false, (true, true) => true }; } // Performs logical OR on an input object public static bool LogicalOr(BooleanInput input) { // Using switch statement switch (input) { case (false, false): return false; case (true, false): return true; case (false, true): return true; case (true, true): return true; } } // Driver code static public void Main() { var a = new BooleanInput{Input1 = true, Input2 = false}; var b = new BooleanInput{Input1 = true, Input2 = true}; Console.WriteLine("Logical AND:"); Console.WriteLine(LogicalAnd(a)); Console.WriteLine(LogicalAnd(b)); Console.WriteLine("Logical OR:"); Console.WriteLine(LogicalOr(a)); Console.WriteLine(LogicalOr(b)); } }
Producción:
Logical AND: False True Logical OR: True True
Ejemplo 2: uso de patrones posicionales con tuplas
Cualquier instancia de System.ValueTuple se puede usar en patrones posicionales. C# proporciona una sintaxis abreviada para crear tuplas usando paréntesis:(). Una tupla se puede crear rápidamente sobre la marcha envolviendo un conjunto de variables ya declaradas entre paréntesis. En el siguiente ejemplo, el método LocatePoint() acepta dos parámetros que representan las coordenadas x e y de un punto y luego crea una tupla después de la palabra clave switch usando un par de paréntesis adicional. Los paréntesis exteriores son parte de la sintaxis de la instrucción switch , los paréntesis interiores crean la tupla usando las variables x e y .
C#
// C# program to illustrate the concept of Positional Pattern using System; class GFG{ // Displays the location of a point // by accepting its x and y coordinates public static void LocatePoint(int x, int y) { Console.WriteLine($"Point ({x}, {y}):"); // Using switch statement // Note the double parantheses switch ((x, y)) { case (0, 0): Console.WriteLine("Point at origin"); break; case (0, _): // _ will match all values for y Console.WriteLine("Point on Y axis"); break; case (_, 0): Console.WriteLine("Point on X axis"); break; default: Console.WriteLine("Point elsewhere"); break; } } // Driver code static public void Main() { LocatePoint(10, 20); LocatePoint(10, 0); LocatePoint(0, 20); LocatePoint(0, 0); } }
Producción:
Point (10, 20): Point elsewhere Point (10, 0): Point on X axis Point (0, 20): Point on Y axis Point (0, 0): Point at origin
Patrón constante
El patrón constante es la forma más simple de un patrón. Consiste en un valor constante. Se comprueba si la expresión que se está emparejando es igual a esta constante. La constante puede ser:
- Un literal numérico, booleano, de caracteres o de string .
- Un valor de enumeración
- nulo.
- Un campo constante .
El patrón constante generalmente aparece como parte de otros patrones como un subpatrón (discutido más adelante), pero también se puede usar solo.
Algunos ejemplos del patrón constante que se usa con el operador is serían:
expression is 2 // int literal
expression is "Geeks" // string literal
expression is System.DayOfWeek.Monday // enum
expression is null // null
Observe el ejemplo final, donde el patrón es nulo . Esto implica que la coincidencia de patrones proporciona otra forma de verificar si un objeto es nulo. Además, expression is null puede ser más legible e intuitivo que la expresión típica == null .
En el contexto de una declaración de cambio , el patrón constante parece idéntico a una declaración de cambio normal sin coincidencia de patrón.
Ejemplo: El siguiente ejemplo usa una expresión de cambio con el patrón constante en un método llamado DayOfTheWeek() que devuelve el nombre del día de la semana a partir del número que se le pasa.
C#
// C# program to illustrate the concept of Constant Pattern using System; class GFG{ // Returns the name of the day of the week public static string DayOfTheWeek(int day) { // Switch expression return day switch { 1 => "Sunday", 2 => "Monday", 3 => "Tuesday", 4 => "Wednesday", 5 => "Thursday", 6 => "Friday", 7 => "Saturday", _ => "Invalid week day" }; } // Driver code static public void Main() { Console.WriteLine(DayOfTheWeek(5)); Console.WriteLine(DayOfTheWeek(3)); } }
Producción:
Thursday Tuesday
El patrón var
El patrón var funciona ligeramente diferente de otros patrones. Una coincidencia de patrón var siempre tiene éxito, lo que significa que el resultado de la coincidencia siempre es verdadero. El propósito del patrón var no es probar una expresión para un patrón, sino asignar una expresión a una variable. Esto permite reutilizar la variable en expresiones consecutivas. El patrón var es una forma más general del patrón de tipo. Sin embargo, no se especifica ningún tipo; en su lugar, se usa var , por lo que no hay verificación de tipos y la coincidencia siempre es exitosa.
Sintaxis:
var varNombre
var (varNombre1, varNombre2, …)
Considere el siguiente código donde se debe comparar el día y el mes de un objeto DateTime :
var now = DateTime.Now; if (now.Month > 6 && now.Day > 15) { // Do Something }
Esto se puede escribir en una línea usando el patrón var :
if (DateTime.Now is var now && now.Month > 6 && now.Day > 15) { // Do Something }
Combinadores de patrones / Patrones lógicos
C# 9 también ha introducido combinadores de patrones. Los combinadores de patrones permiten combinar varios patrones juntos. Los siguientes son los combinadores de patrones:
- Patrón negativo: no
- Patrón Conjuntivo: y
- Patrón disyuntivo o
combinador | Palabra clave | Descripción | Ejemplo |
---|---|---|---|
patrón negativo | no | Invierte una coincidencia de patrón |
no 2 no < 10 no nulo |
Patrón Conjuntivo | y | Coincide si ambos patrones coinciden |
> 0 y < 10 { Año: 2002 } y { Mes: 1 } no int y no doble |
patrón disyuntivo | o | Coincide si al menos uno de los patrones coincide |
«Hola» o «Hola» o «Hola» nulo o (0, 0) { Año: 2004 } o { Año: 2002 } |
Los combinadores de patrones se parecen mucho a los operadores lógicos (!, &&, ||) pero los operandos son patrones en lugar de condiciones o expresiones booleanas. Los combinadores hacen que la coincidencia de patrones sea más flexible y también ayuda a ahorrar algunas pulsaciones de teclas.
Comparaciones más simples
Mediante el uso de combinadores de patrones con el patrón relacional, una expresión se puede comparar con muchos otros valores sin repetir la expresión una y otra vez. Por ejemplo, considere lo siguiente:
int number = 42; if (number > 10 && number < 50 && number != 35) { // Do Something }
Con la coincidencia de patrones y los combinadores, esto se puede simplificar:
int number = 42; if (number is > 10 and < 50 and not 35) { // Do Something }
Como se puede observar, no es necesario repetir el número del nombre de la variable ; las comparaciones se pueden enstringr sin problemas.
Comprobar si un valor no es nulo
En la sección de patrones constantes anterior, se discutió una forma alternativa de verificar si un valor es nulo. Los combinadores de patrones proporcionan una contraparte que permite verificar si un valor no es nulo:
if (expression is not null) { }
Ejemplo: El siguiente ejemplo define un método llamado I sVowel() que verifica si un carácter es una vocal o no usando el combinador de patrones o para combinar múltiples patrones constantes:
C#
// C# program to illustrate the concept of Pattern Combinators using System; class GFG{ public static bool IsVowel(char c) { return char.ToLower(c) is 'a' or 'e' or 'i' or 'o' or 'u'; } // Driver code public static void Main() { Console.WriteLine(IsVowel('A')); Console.WriteLine(IsVowel('B')); Console.WriteLine(IsVowel('e')); Console.WriteLine(IsVowel('x')); Console.WriteLine(IsVowel('O')); Console.WriteLine(IsVowel('P')); } }
Producción:
True False True False True False
Declaraciones de variables
Algunos patrones admiten declarar una variable después del patrón.
Patrón de tipo: las declaraciones de variables en los patrones de tipo son una forma conveniente de combinar una verificación de tipo y una conversión en un solo paso.
Considera lo siguiente:
object o = 42; if (o is int) { int i = (int) o; //... }
Esto se puede reducir a un solo paso usando una declaración de variable:
object o = 42; if (o is int i) { //... }
Patrones posicionales y de propiedades: los patrones posicionales y de propiedades también permiten una declaración de variables después del patrón:
if (DateTime.Now is { Month: 12 } now) { // Do something with now }
var p = (10, 20); if (p is (10, 20) coords) { // Do something with coords }
Aquí, p y coords contienen el mismo valor y las coords pueden ser de poca utilidad. Pero la sintaxis anterior es legal y, a veces, puede ser útil con un objeto que define un método Deconstruct() .
Nota: Las declaraciones de variables no están permitidas cuando se usan los combinadores de patrones o y no , pero sí con y .
Descartes Variables
A veces, el valor asignado a las variables durante la coincidencia de patrones puede no ser útil. Los descartes de variables permiten ignorar los valores de tales variables. Un descarte se representa mediante un guión bajo ( _ ).
En patrones de tipos: cuando se usan patrones de tipos con declaraciones de variables como en el siguiente ejemplo:
switch (expression) { case int i: Console.WriteLine("i is an integer"); break; ... }
la variable i nunca se usa. Por lo tanto, se puede descartar:
case int _:
A partir de C# 9, es posible usar un patrón de tipo sin una variable, lo que también permite eliminar el guión bajo:
case int:
En patrones posicionales: cuando se usan patrones posicionales, se puede usar un descarte como carácter comodín para hacer coincidir todos los valores en ciertas posiciones. Por ejemplo, en el ejemplo de punto anterior, si queremos hacer coincidir cuando la coordenada x es 0 y la coordenada y no importa, lo que significa que el patrón debe coincidir sin importar cuál sea la coordenada y, lo siguiente puede ser hecho:
point is (0, _) // will match for all values of y
Patrones múltiples sin combinadores
Es posible usar un patrón de tipo, un patrón posicional y un patrón de propiedad juntos sin combinadores.
Sintaxis:
tipo-patrón patrón-posicional patrón-propiedad nombre-variable
Considere la estructura Point :
struct Point { public int X { get; set; } public int Y { get; set; } public string Name { get; set; } public void Deconstruct(out int x, out int y) { x = X; y = Y; } } ... object o = Point() { X = 10, Y = 20, Name = "A" };
En vez de:
if (o is Point p and (10, _) and { Y: 20}) {..}
Se puede escribir lo siguiente:
if (o is Point (10, _) { Y: 20 } p) {..}
Se pueden omitir uno o más de los patrones. Sin embargo, debe haber al menos un patrón, y el orden cuando se usan varios patrones juntos debe ser el mismo que el anterior. Por ejemplo, lo siguiente es ilegal:
if (o is (10, _) Point { Y: 20 } p)
Patrones/subpatrones anidados
Un patrón puede constar de varios subpatrones. Además de la capacidad de combinar múltiples patrones con combinadores de patrones, C# también permite que un solo patrón se componga de varios patrones internos.
Ejemplos:
Considere la estructura Point anterior y el siguiente punto de objeto :
Point point = new Point() { X = 10, Y = 20, Name = "B" };
Type Pattern y var Pattern en un patrón de propiedad
if (point is { X: int x, Y: var y }) { .. }
Escriba el patrón y el patrón var en un patrón posicional
if (point is (int x, var y)) { .. }
Patrón relacional en un patrón posicional y un patrón de propiedad
switch (point) { case (< 10, <= 15): .. break; case { X: < 10, Y: <= 15 }: .. break; .. }
Para el siguiente ejemplo, considere los siguientes objetos:
var p1 = new Point { X = 0, Y = 1, Name = "X" }; var p2 = new Point { X = 0, Y = 2, Name = "Y" };
Patrón de propiedades en un patrón posicional
if ((p1, p2) is ({ X: 0 }, { X: 0 })) { .. }
Publicación traducida automáticamente
Artículo escrito por amallkrishna y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA