Coincidencia de patrones: las expresiones is y switch y los operadores and, or y not en patrones.

La expresión is, la instrucción switch y la expresión switch se usan para comparar una expresión de entrada con cualquier número de características. C# admite varios patrones, como declaración, tipo, constante, relacional, propiedad, lista, var y descarte. Los patrones se pueden combinar mediante las palabras clave lógica booleanas and, or y not.

Las siguientes expresiones e instrucciones de C# admiten la coincidencia de patrones:

En esas construcciones, puede hacer coincidir una expresión de entrada con cualquiera de los siguientes patrones:

  • Patrón de declaración: para comprobar el tipo en tiempo de ejecución de una expresión y, si una coincidencia se realiza correctamente, asignar el resultado de una expresión a una variable declarada.
  • Patrón de tipo: para comprobar el tipo en tiempo de ejecución de una expresión.
  • Patrón de constante: para probar si el resultado de una expresión es igual a una constante especificada.
  • Patrones relacionales: para comparar el resultado de una expresión con una constante especificada.
  • Patrones lógicos: para probar si una expresión coincide con una combinación lógica de patrones.
  • Patrón de propiedad: para probar si las propiedades o los campos de una expresión coinciden con los patrones anidados.
  • Patrón posicional: para deconstruir el resultado de una expresión y probar si los valores resultantes coinciden con los patrones anidados.
  • Patrón var: para buscar coincidencias con cualquier expresión y asignar su resultado a una variable declarada.
  • Patrón de descarte: para buscar coincidencias con cualquier expresión.
  • Patrones de lista: para probar si los elementos de la secuencia coinciden con los patrones anidados correspondientes. Presentado en C# 11.

Los patrones lógicos, de propiedad, posicionales y de lista son patrones recursivos. Es decir, pueden contener patrones anidados.

Para obtener el ejemplo de cómo usar esos patrones para compilar un algoritmo basado en datos, vea Tutorial: Uso de la coincidencia de patrones para compilar algoritmos basados en tipos y basados en datos.

Patrones de declaración y de tipo

Los patrones de declaración y de tipo se usan para comprobar si el tipo en tiempo de ejecución de una expresión es compatible con un tipo determinado. Con un patrón de declaración, también puede declarar una nueva variable local. Cuando un patrón de declaración coincide con una expresión, a esa variable se le asigna el resultado de una expresión convertida, como se muestra en el ejemplo siguiente:

object greeting = "Hello, World!";
if (greeting is string message)
{
    Console.WriteLine(message.ToLower());  // output: hello, world!
}

Un patrón de declaración con el tipo T coincide con una expresión cuando el resultado de una expresión no es NULL y se cumple cualquiera de las condiciones siguientes:

  • El tipo en tiempo de ejecución del resultado de una expresión es T.

  • El tipo en tiempo de ejecución del resultado de una expresión deriva del tipo T, implementa una interfaz T, o bien otra conversión de referencia implícita existe en T. En el ejemplo siguiente se muestran dos casos en los que esta condición es verdadera:

    var numbers = new int[] { 10, 20, 30 };
    Console.WriteLine(GetSourceLabel(numbers));  // output: 1
    
    var letters = new List<char> { 'a', 'b', 'c', 'd' };
    Console.WriteLine(GetSourceLabel(letters));  // output: 2
    
    static int GetSourceLabel<T>(IEnumerable<T> source) => source switch
    {
        Array array => 1,
        ICollection<T> collection => 2,
        _ => 3,
    };
    

    En el ejemplo anterior, en la primera llamada al método GetSourceLabel, el primer patrón coincide con un valor de argumento porque el tipo en tiempo de ejecución int[] del argumento deriva del tipo Array. En la segunda llamada al método GetSourceLabel, el tipo en tiempo de ejecución List<T> del argumento no deriva del tipo Array, pero implementa la interfaz ICollection<T>.

  • El tipo en tiempo de ejecución del resultado de una expresión es un tipo de valor que admite valores NULL con el tipo subyacente T.

  • Existe una conversión boxing o unboxing del tipo en tiempo de ejecución del resultado de una expresión al tipo T.

En el ejemplo siguiente se muestran las dos últimas condiciones:

int? xNullable = 7;
int y = 23;
object yBoxed = y;
if (xNullable is int a && yBoxed is int b)
{
    Console.WriteLine(a + b);  // output: 30
}

Si solo desea comprobar el tipo de una expresión, puede usar un patrón de descarte _ en lugar del nombre de una variable, como se muestra en el ejemplo siguiente:

public abstract class Vehicle {}
public class Car : Vehicle {}
public class Truck : Vehicle {}

public static class TollCalculator
{
    public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
    {
        Car _ => 2.00m,
        Truck _ => 7.50m,
        null => throw new ArgumentNullException(nameof(vehicle)),
        _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
    };
}

Para ello, puede usar un patrón de tipo, como se muestra en el ejemplo siguiente:

public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
{
    Car => 2.00m,
    Truck => 7.50m,
    null => throw new ArgumentNullException(nameof(vehicle)),
    _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
};

Al igual que un patrón de declaración, un patrón de tipo coincide con una expresión cuando el resultado de una expresión no es NULL y su tipo en tiempo de ejecución cumple cualquiera de las condiciones mencionadas anteriormente.

Para comprobar si no es NULL, puede usar un patrón de constante null negada, como se muestra en el ejemplo siguiente:

if (input is not null)
{
    // ...
}

Para obtener más información, vea las secciones Patrón de declaración y Patrón de tipo de las notas de propuesta de características.

Patrón de constante

Utilice un patrón de constante para probar si el resultado de una expresión es igual a una constante especificada, como se muestra en el ejemplo siguiente:

public static decimal GetGroupTicketPrice(int visitorCount) => visitorCount switch
{
    1 => 12.0m,
    2 => 20.0m,
    3 => 27.0m,
    4 => 32.0m,
    0 => 0.0m,
    _ => throw new ArgumentException($"Not supported number of visitors: {visitorCount}", nameof(visitorCount)),
};

En un patrón de constante, se puede usar cualquier expresión constante, como:

La expresión debe ser un tipo que se puede convertir al tipo constante, con una excepción: una expresión cuyo tipo es Span<char> o ReadOnlySpan<char> se puede comparar con cadenas constantes en C# 11 y versiones posteriores.

Use un patrón de constante para comprobar null, como se muestra en el ejemplo siguiente:

if (input is null)
{
    return;
}

El compilador garantiza que no se invoca ningún operador de igualdad sobrecargado por el usuario == cuando se evalúa la expresión x is null.

Puede usar un patrón de constantes negadasnull para comprobar si no son NULL, como se muestra en el ejemplo siguiente:

if (input is not null)
{
    // ...
}

Para obtener más información, vea la sección Patrón de constante de la nota de propuesta de características.

Patrones relacionales

Use un patrón relacional para comparar un resultado de expresión con una constante, como se muestra en el ejemplo siguiente:

Console.WriteLine(Classify(13));  // output: Too high
Console.WriteLine(Classify(double.NaN));  // output: Unknown
Console.WriteLine(Classify(2.4));  // output: Acceptable

static string Classify(double measurement) => measurement switch
{
    < -4.0 => "Too low",
    > 10.0 => "Too high",
    double.NaN => "Unknown",
    _ => "Acceptable",
};

En un patrón relacional, se puede usar cualquiera de los operadores relacionales <, >, <= o >=. La parte derecha de un patrón relacional debe ser una expresión constante. La expresión constante puede ser de tipo entero, de punto flotante, de carácter o de enumeración.

Para comprobar si el resultado de una expresión está en un intervalo determinado, busque coincidencias con un patrón conjuntivo and, como se muestra en el ejemplo siguiente:

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 3, 14)));  // output: spring
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 7, 19)));  // output: summer
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 2, 17)));  // output: winter

static string GetCalendarSeason(DateTime date) => date.Month switch
{
    >= 3 and < 6 => "spring",
    >= 6 and < 9 => "summer",
    >= 9 and < 12 => "autumn",
    12 or (>= 1 and < 3) => "winter",
    _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
};

Si el resultado de una expresión es null o no se puede convertir al tipo de una constante mediante una conversión que acepta valores NULL o unboxing, un patrón relacional no coincide con una expresión.

Para obtener más información, vea la sección Patrones relacionales de la nota de propuesta de características.

Patrones lógicos

Use los combinadores de patrones not, andy or para crear los siguientes patrones lógicos:

  • Patrón de negación not que coincide con una expresión cuando el patrón negado no coincide con ella. En el ejemplo siguiente se muestra cómo se puede negar un patrón de constante null para comprobar si una expresión no es NULL:

    if (input is not null)
    {
        // ...
    }
    
  • Patrón conjuntivo and que coincide con una expresión cuando ambos patrones coinciden con ella. En el ejemplo siguiente se muestra cómo se pueden combinar patrones relacionales para comprobar si un valor se encuentra en un intervalo determinado:

    Console.WriteLine(Classify(13));  // output: High
    Console.WriteLine(Classify(-100));  // output: Too low
    Console.WriteLine(Classify(5.7));  // output: Acceptable
    
    static string Classify(double measurement) => measurement switch
    {
        < -40.0 => "Too low",
        >= -40.0 and < 0 => "Low",
        >= 0 and < 10.0 => "Acceptable",
        >= 10.0 and < 20.0 => "High",
        >= 20.0 => "Too high",
        double.NaN => "Unknown",
    };
    
  • Patrón disyuntivoor que coincide con una expresión cuando uno de los patrones coincide con ella, como se muestra en el ejemplo siguiente:

    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));  // output: winter
    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));  // output: autumn
    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));  // output: spring
    
    static string GetCalendarSeason(DateTime date) => date.Month switch
    {
        3 or 4 or 5 => "spring",
        6 or 7 or 8 => "summer",
        9 or 10 or 11 => "autumn",
        12 or 1 or 2 => "winter",
        _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
    };
    

Como se muestra en el ejemplo anterior, se puede usar repetidamente los combinadores de patrones en un patrón.

Precedencia y orden de comprobación

Los combinadores de patrones se ordenan de mayor a menor precedencia de la siguiente manera:

  • not
  • and
  • or

Cuando un patrón lógico es un patrón de una expresión is, la prioridad de los combinadores de patrones lógicos es mayor que la prioridad de los operadores lógicos (operadores lógicos bit a bit y booleanos ). De lo contrario, la prioridad de los combinadores de patrones lógicos es menor que la prioridad de los operadores lógicos y condicionales. Para obtener la lista completa de los operadores de C# ordenados por nivel de prioridad, vea la sección Prioridad de operadores del artículo Operadores de C#.

Para especificar explícitamente la prioridad, use paréntesis, como se muestra en el ejemplo siguiente:

static bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');

Nota

El orden en el que se comprueban los patrones es indefinido. En tiempo de ejecución, se pueden comprobar primero los patrones anidados del lado derecho de los patrones or y and.

Para obtener más información, vea la sección Combinadores de patrones de la nota de propuesta de características.

Patrón de propiedad

Utilice un patrón de propiedad para hacer coincidir las propiedades o los campos de una expresión con los patrones anidados, como se muestra en el ejemplo siguiente:

static bool IsConferenceDay(DateTime date) => date is { Year: 2020, Month: 5, Day: 19 or 20 or 21 };

Un patrón de propiedad coincide con una expresión cuando el resultado de una expresión no es NULL y cada patrón anidado coincide con la propiedad o el campo correspondiente del resultado de la expresión.

También puede agregar una comprobación de tipo en tiempo de ejecución y una declaración de variable a un patrón de propiedad, como se muestra en el ejemplo siguiente:

Console.WriteLine(TakeFive("Hello, world!"));  // output: Hello
Console.WriteLine(TakeFive("Hi!"));  // output: Hi!
Console.WriteLine(TakeFive(new[] { '1', '2', '3', '4', '5', '6', '7' }));  // output: 12345
Console.WriteLine(TakeFive(new[] { 'a', 'b', 'c' }));  // output: abc

static string TakeFive(object input) => input switch
{
    string { Length: >= 5 } s => s.Substring(0, 5),
    string s => s,

    ICollection<char> { Count: >= 5 } symbols => new string(symbols.Take(5).ToArray()),
    ICollection<char> symbols => new string(symbols.ToArray()),

    null => throw new ArgumentNullException(nameof(input)),
    _ => throw new ArgumentException("Not supported input type."),
};

Un patrón de propiedad es un patrón recursivo. Es decir, se puede usar cualquier patrón como patrón anidado. Use un patrón de propiedad para hacer coincidir partes de los datos con patrones anidados, como se muestra en el ejemplo siguiente:

public record Point(int X, int Y);
public record Segment(Point Start, Point End);

static bool IsAnyEndOnXAxis(Segment segment) =>
    segment is { Start: { Y: 0 } } or { End: { Y: 0 } };

En el ejemplo anterior se usa el orcombinador de patrones y los tipos de registro.

A partir de C# 10, puede hacer referencia a propiedades o campos anidados en un patrón de propiedad. Esta funcionalidad se conoce como patrón de propiedad extendida. Por ejemplo, puede refactorizar el método del ejemplo anterior en el código equivalente siguiente:

static bool IsAnyEndOnXAxis(Segment segment) =>
    segment is { Start.Y: 0 } or { End.Y: 0 };

Para obtener más información, consulte la sección Patrón de propiedad de la nota de propuesta de características y la nota de propuesta de características Patrones de propiedades extendidos.

Sugerencia

Puede usar la regla de estilo Simplificar patrón de propiedades (IDE0170) para mejorar la legibilidad del código mediante la sugerencia de lugares para usar patrones de propiedades extendidos.

Patrón posicional

Utilice un patrón posicional para deconstruir el resultado de una expresión y hacer coincidir los valores resultantes con los patrones anidados correspondientes, como se muestra en el ejemplo siguiente:

public readonly struct Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) => (X, Y) = (x, y);

    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

static string Classify(Point point) => point switch
{
    (0, 0) => "Origin",
    (1, 0) => "positive X basis end",
    (0, 1) => "positive Y basis end",
    _ => "Just a point",
};

En el ejemplo anterior, el tipo de una expresión contiene el método Deconstruct, que se usa para deconstruir el resultado de una expresión.

Importante

El orden de los miembros de un patrón posicional debe coincidir con el orden de los parámetros del método Deconstruct. Esto se debe a que el código generado para el patrón posicional llama al método Deconstruct.

También puede hacer coincidir expresiones de tipos de tupla con patrones posicionales. De este modo, puede hacer coincidir varias entradas con distintos patrones, como se muestra en el ejemplo siguiente:

static decimal GetGroupTicketPriceDiscount(int groupSize, DateTime visitDate)
    => (groupSize, visitDate.DayOfWeek) switch
    {
        (<= 0, _) => throw new ArgumentException("Group size must be positive."),
        (_, DayOfWeek.Saturday or DayOfWeek.Sunday) => 0.0m,
        (>= 5 and < 10, DayOfWeek.Monday) => 20.0m,
        (>= 10, DayOfWeek.Monday) => 30.0m,
        (>= 5 and < 10, _) => 12.0m,
        (>= 10, _) => 15.0m,
        _ => 0.0m,
    };

En el ejemplo anterior se usan patrones relacionales y lógicos.

Puede usar los nombres de elementos de tupla y parámetros Deconstruct en un patrón posicional, como se muestra en el ejemplo siguiente:

var numbers = new List<int> { 1, 2, 3 };
if (SumAndCount(numbers) is (Sum: var sum, Count: > 0))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}");  // output: Sum of [1 2 3] is 6
}

static (double Sum, int Count) SumAndCount(IEnumerable<int> numbers)
{
    int sum = 0;
    int count = 0;
    foreach (int number in numbers)
    {
        sum += number;
        count++;
    }
    return (sum, count);
}

También puede extender un patrón posicional de cualquiera de las siguientes maneras:

  • Agregue una comprobación de tipo en tiempo de ejecución y una declaración de variable, como se muestra en el ejemplo siguiente:

    public record Point2D(int X, int Y);
    public record Point3D(int X, int Y, int Z);
    
    static string PrintIfAllCoordinatesArePositive(object point) => point switch
    {
        Point2D (> 0, > 0) p => p.ToString(),
        Point3D (> 0, > 0, > 0) p => p.ToString(),
        _ => string.Empty,
    };
    

    En el ejemplo anterior se usan registros posicionales que proporcionan implícitamente el método Deconstruct.

  • Use un patrón de propiedad dentro de un patrón posicional, como se muestra en el ejemplo siguiente:

    public record WeightedPoint(int X, int Y)
    {
        public double Weight { get; set; }
    }
    
    static bool IsInDomain(WeightedPoint point) => point is (>= 0, >= 0) { Weight: >= 0.0 };
    
  • Combine dos usos anteriores, como se muestra en el ejemplo siguiente:

    if (input is WeightedPoint (> 0, > 0) { Weight: > 0.0 } p)
    {
        // ..
    }
    

Un patrón posicional es un patrón recursivo. Es decir, se puede usar cualquier patrón como patrón anidado.

Para obtener más información, vea la sección Patrón posicional de la nota de propuesta de características.

Patrón var

Utilice un patrón var para buscar coincidencias con cualquier expresión, incluida null, y asignar el resultado a una nueva variable local, como se muestra en el ejemplo siguiente:

static bool IsAcceptable(int id, int absLimit) =>
    SimulateDataFetch(id) is var results 
    && results.Min() >= -absLimit 
    && results.Max() <= absLimit;

static int[] SimulateDataFetch(int id)
{
    var rand = new Random();
    return Enumerable
               .Range(start: 0, count: 5)
               .Select(s => rand.Next(minValue: -10, maxValue: 11))
               .ToArray();
}

Un patrón var es útil cuando se necesita una variable temporal dentro de una expresión booleana para contener el resultado de los cálculos intermedios. También se puede usar un patrón var cuando necesite realizar comprobaciones adicionales en las restricciones de mayúsculas y minúsculas when de una expresión o instrucción switch, como se muestra en el ejemplo siguiente:

public record Point(int X, int Y);

static Point Transform(Point point) => point switch
{
    var (x, y) when x < y => new Point(-x, y),
    var (x, y) when x > y => new Point(x, -y),
    var (x, y) => new Point(x, y),
};

static void TestTransform()
{
    Console.WriteLine(Transform(new Point(1, 2)));  // output: Point { X = -1, Y = 2 }
    Console.WriteLine(Transform(new Point(5, 2)));  // output: Point { X = 5, Y = -2 }
}

En el ejemplo anterior, el patrón var (x, y) es equivalente a un patrón posicional (var x, var y).

En un patrón var, el tipo de una variable declarada es el tipo en tiempo de compilación de la expresión que coincide con el patrón.

Para obtener más información, vea la sección Patrón Var de la nota de propuesta de características.

Patrón de descarte

Utilice un patrón de descarte _ para buscar coincidencias con cualquier expresión, incluida null, como se muestra en el ejemplo siguiente:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));  // output: 5.0
Console.WriteLine(GetDiscountInPercent(null));  // output: 0.0
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));  // output: 0.0

static decimal GetDiscountInPercent(DayOfWeek? dayOfWeek) => dayOfWeek switch
{
    DayOfWeek.Monday => 0.5m,
    DayOfWeek.Tuesday => 12.5m,
    DayOfWeek.Wednesday => 7.5m,
    DayOfWeek.Thursday => 12.5m,
    DayOfWeek.Friday => 5.0m,
    DayOfWeek.Saturday => 2.5m,
    DayOfWeek.Sunday => 2.0m,
    _ => 0.0m,
};

En el ejemplo anterior, se usa un patrón de descarte para controlar null y cualquier valor entero que no tenga el miembro correspondiente de la enumeración DayOfWeek. Esto garantiza que una expresión switch en el ejemplo controle todos los valores de entrada posibles. Si no se usa un patrón de descarte en una expresión switch y ninguno de los patrones de la expresión coincide con una entrada, el tiempo de ejecución produce una excepción. El compilador genera una advertencia si una expresión switch no controla todos los valores de entrada posibles.

Un patrón de descarte no puede ser un patrón en una expresión is ni una instrucción switch. En esos casos, para buscar coincidencias con cualquier expresión, use un patrón var con un patrón de descarte: var _. Un patrón de descarte puede ser un patrón en una expresión switch.

Para obtener más información, vea la sección Patrón de descarte de la nota de propuesta de características.

Patrón entre paréntesis

Puede colocar paréntesis alrededor de cualquier patrón. Normalmente, esto se hace para resaltar o cambiar la prioridad en patrones lógicos, como se muestra en el ejemplo siguiente:

if (input is not (float or double))
{
    return;
}

Patrones de lista

A partir de C# 11, puede buscar coincidencias de una matriz o una lista con una secuencia de patrones, como se muestra en el ejemplo siguiente:

int[] numbers = { 1, 2, 3 };

Console.WriteLine(numbers is [1, 2, 3]);  // True
Console.WriteLine(numbers is [1, 2, 4]);  // False
Console.WriteLine(numbers is [1, 2, 3, 4]);  // False
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]);  // True

Como se muestra en el ejemplo anterior, un patrón de lista coincide cuando cada patrón anidado coincide con el elemento correspondiente de una secuencia de entrada. Puede usar cualquier patrón dentro de un patrón de lista. Para buscar coincidencias con cualquier elemento, use el patrón de descarte o, si también desea capturar el elemento, el patrón var, como se muestra en el ejemplo siguiente:

List<int> numbers = new() { 1, 2, 3 };

if (numbers is [var first, _, _])
{
    Console.WriteLine($"The first element of a three-item list is {first}.");
}
// Output:
// The first element of a three-item list is 1.

Los ejemplos anteriores buscan coincidencias de una secuencia de entrada completa con un patrón de lista. Para buscar coincidencias con los elementos solo al principio o al final de una secuencia de entrada, use el patrón de sector.., como se muestra en el ejemplo siguiente:

Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True
Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True
Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False
Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False

Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True
Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False
Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True

Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]);  // True
Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]);  // True
Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]);  // False

Un patrón de segmento busca coincidencias con cero o más elementos. Puede usar como máximo un patrón de segmento en un patrón de lista. El patrón de sector solo puede aparecer en un patrón de lista.

También puede anidar un subpatrón dentro de un patrón de segmento, como se muestra en el ejemplo siguiente:

void MatchMessage(string message)
{
    var result = message is ['a' or 'A', .. var s, 'a' or 'A']
        ? $"Message {message} matches; inner part is {s}."
        : $"Message {message} doesn't match.";
    Console.WriteLine(result);
}

MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.
MatchMessage("apron");  // output: Message apron doesn't match.

void Validate(int[] numbers)
{
    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0] ? "valid" : "not valid";
    Console.WriteLine(result);
}

Validate(new[] { -1, 0, 1 });  // output: not valid
Validate(new[] { -1, 0, 0, 1 });  // output: valid

Para más información, consulte la nota de propuesta de características para patrones de lista.

Especificación del lenguaje C#

Para más información, consulte la sección Patrones y coincidencia de patrones de la especificación del lenguaje C#.

Para más información sobre de las características agregadas a C# 8 y versiones posteriores, consulte las siguientes notas de propuesta de características:

Consulte también