Паттерны списков

Последнее обновление: 27.02.2022

Паттерны списков (list pattern) позволяют сопоставлять выражения со списками и массивами. Данный паттерн пока доступен начиная с версии C# 11.

Полное совпадение с массивом/списком:

Console.WriteLine(GetNumber(new[] { 1, 2, 3, 4, 5 }));  // 1
Console.WriteLine(GetNumber(new[] { 1, 2}));            // 3
Console.WriteLine(GetNumber(new int[] {}));             // 4
Console.WriteLine(GetNumber(new[] { 1, 2, 5 }));        // 5

int GetNumber(int[] values)=> values switch
{
        [1, 2, 3, 4, 5] => 1,
        [1, 2, 3] => 2,
        [1, 2] => 3,
        [] => 4,
        _ => 5
};

Аналогично вместо массивов можно применять списки:

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

Console.WriteLine(GetNumber(numbers));  // 2

int GetNumber(List<int> values) => values switch
{
    [1, 2, 3, 4, 5] => 1,
    [1, 2, 3] => 2,
    [1, 2] => 3,
    [] => 4,
    _ => 5
};

Аналогичным образом паттерны списков можно использовать в конструкции if:

int[] numbers = { 1, 2, 3, 4, 5 };
if(numbers is [1, 2, 3, 4, 5])
{
    Console.WriteLine("[1, 2, 3, 4, 5]");
}

Подстановка _

С помощью паттерна _ можно обозначить одиночный элемент, который имеет любое значение. Например, паттерн [2, _, 5] соответствует любому массиву из трех элементов, в котором между 2 и 5 располагается произвольное значение. А массив [_, _] соответствует любому массиву из двух произвольных элементов

Несколько примеров:

Console.WriteLine(GetNumber(new[] { 2, 3, 5}));       // 1
Console.WriteLine(GetNumber(new[] { 2, 4, 6 }));      // 2
Console.WriteLine(GetNumber(new[] { 1, 2, 5 }));      // 3
Console.WriteLine(GetNumber(new[] { 1, 2, 3}));      // 4
Console.WriteLine(GetNumber(new int[] { }));         // 5

int GetNumber(int[] values) => values switch
{
    [2, _, 5] => 1,
    [2, _, _] => 2,
    [_, _, 5] => 3,
    [_, _, _] => 4,
    _ => 5
};

slice-паттерн

Для передачи произвольного количества элементов массива/списка применяется slice-паттерн ... Например, паттерн [1, 2, .., 5] соответствует массиву, в который начинается на 1, за которым идет 2. А последний элемент в массиве - 5. При этом между 2 и 5 может располагаться произвольное количество произвольных целых чисел. То есть паттерн [1, 2, .., 5] будет соответствовать таким массивам как

int[] arr1 = {  1, 2, 3, 4, 5 };
int[] arr2 = { 1, 2, 5 };
int[] arr3 = { 1, 2, 66, 77, 88, 5 };

С помощью паттерна .. можно задавать произвольное количество элементов как в начале, так и в конце массива/списка. Например, паттерн [2,..] представляет массив, который начинается на 2. А паттерн [.., 5] представляет массив, который заканчивается элементом 5. Паттерн [..] представляет массив, который содержит произвольное количество элементов. Например:

Console.WriteLine(GetNumber(new[] { 2, 5 }));           // 1
Console.WriteLine(GetNumber(new[] { 2, 3, 4, 5 }));     // 1

Console.WriteLine(GetNumber(new[] { 2}));               // 2
Console.WriteLine(GetNumber(new[] { 2, 3, 4}));         // 2

Console.WriteLine(GetNumber(new[] {3, 4, 5}));          // 3
Console.WriteLine(GetNumber(new[] { 5 }));              // 3

Console.WriteLine(GetNumber(new int[] {  }));           // 4
Console.WriteLine(GetNumber(new[] { 1 }));              // 4
Console.WriteLine(GetNumber(new[] { 1, 2, 3 }));        // 4

int GetNumber(int[] values)=> values switch
{
    [2, .., 5] => 1,    // если первый элемент - 2, а последний - 5
    [2,..] => 2,        // если первый элемент - 2
    [.., 5] => 3,       // если последний элемент - 5
    [..] => 4          // произвольное количество элементов
};

slice-паттерн можно сочетать с символов подстановки _, например:

int GetNumber(int[] values) => values switch
{
    [_, .., _] => 1,
    [..] => 2
};

В данном случае паттерн [_, .., _] предполагает массив, который состоит как минимум из двух произвольных элементов, и между первым и последним элементром может находиться произвольное количество других элементов:

Console.WriteLine(GetNumber(new[] { 1, 2, 3, 4}));   // 1
Console.WriteLine(GetNumber(new[] { 1, 2, 3}));     // 1
Console.WriteLine(GetNumber(new[] { 1, 2}));        // 1
Console.WriteLine(GetNumber(new[] { 1 }));          // 2
Console.WriteLine(GetNumber(new int[] { }));       // 2

int GetNumber(int[] values) => values switch
{
    [_, .., _] => 1,
    [..] => 2
};

Получение элементов в переменные

Отдельные значения массива/списка можно получить в переменные, например:

int[] numbers = { 2, 3, 5 };
if(numbers is [var first, var second, .., var last])
{
    Console.WriteLine($"first: {first}, second: {second}  last: {last}");
}

В данном случае получаем первый элемент массива в переменную first, второй элемент - в переменную second, а последний элемент - в переменную last.

Пример с различными массивами:

Console.WriteLine(GetData(new[] { 1, 2, 3 }));        // First: 1  Second: 2  Last: 3
Console.WriteLine(GetData(new[] { 2, 4, 6, 8 }));    // First: 2  Second: 4  Last: 8
Console.WriteLine(GetData(new[] { 1, 2 }));          // Array has less than 3 elements

string GetData(int[] values) => values switch
{
    [var first, var second, .., var last] => $"First: {first}  Second: {second}  Last: {last}",
    [..] => "Array has less than 3 elements"
};

В данном случае получаем первый элемент массива в переменную first, второй элемент - в переменную second, а последний элемент - в переменную last.

При этом значения, которые проектируются на паттерн .., также можно получить в переменную. Например, в паттерне [2, .. var middle, 5] элементы, которые проектируются на .., можно передаются в переменную middle. Несколько примеров:

Console.WriteLine(GetSlice(new[] { 2, 3, 4, 5}));       // Middle: 3, 4
Console.WriteLine(GetSlice(new[] { 2, 4, 6, 8 }));      // End: 4, 6, 8
Console.WriteLine(GetSlice(new[] { 1, 2, 3, 5 }));      // Start: 1, 2, 3
Console.WriteLine(GetSlice(new[] { 1, 2, 3, 4 }));      // All: 1, 2, 3, 4
Console.WriteLine(GetSlice(new int[] { }));             // All: 

string GetSlice(int[] values) => values switch
{
    [2, .. var middle, 5] => $"Middle: {string.Join(", ", middle)}",
    [2, .. var end] => $"End: {string.Join(", ", end)}",
    [.. var start, 5] => $"Start: {string.Join(", ", start)}",
    [.. var all] => $"All: {string.Join(", ", all)}"
};

Свойства коллекций

Стоит отметить, что, поскольку массивы и списки - обычные классы C#, которые имеют свойства, то для них мы также можем применять паттерн свойств. Объединение паттерна свойств и паттерна списков позволяет упростить решение некоторых задач. Например, у нас есть задача: если массив имеет три элемента, то разложить его на три переменных:

int[] numbers = { 2, 3, 5 };
if(numbers is { Length:3} and [var first, var second, var third])
{
    Console.WriteLine($"first: {first}, second: {second}  third: {third}");
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850