Andrey on .NET | C# 9 – Сопоставление с образцом (pattern matсhing)

C# 9 – Сопоставление с образцом (pattern matсhing)

C# logoСопоставление с образцом не является новой возможностью. Оно появилось еще в C# 7 и было улучшено в C# 8. В 9 версии языка появились новые образцы, которые расширяют возможности сопоставлений и делают их более удобными для чтения.

Минимальная платформа с полной поддержкой: нет ограничений.

Для начала перечислим какие образцы уже поддерживаются:

Как и прежде, образцы могут быть использованы в конструкциях is, switch/case и выражении switch.

Упрощение образца типа

В предыдущих версиях C# в образце типа всегда должен был присутсвовать идентификатор или его подстановка _. Начиная с C# 9 это является не обязательным.

Теперь можно использовать следующий формат записи:

[тип] [опциональный идентификатор новой переменной]

Например:

public class Circle { … }
…
var result = obj switch {
    …
    Circle  => throw new NotSupportedException(),
    // Для C# 8 и более ранних версий должно быть:
    // Circle _ => throw new NotSupportedException(),
    …
}

Образцы соотношения

Эти образцы предназначены для проверки соотношение указанного значения из другого образца с константой. Формат записи:

[операция соотношения] [константа]

, где доступны следующие операции:

  • < – меньше;
  • <= – меньше или равно;
  • => – больше или равно;
  • > – больше.

Используя их совместно с другими образцами можно создавать достаточно короткие, но выразительные проверки, например:

public record Point(int X, int Y);
…
bool isPositive = point is { X: > 0, Y: > 0 };

Данное сопоставление вернет true если значения свойств X и Y  переменной point больше нуля. Как можно заметить, здесь образец соотношения вложен в образец свойств.

Логические образцы

В C# 9 добавились образцы, которые обеспечивают логические операции между другими образцами:

  • and – логическое "и";
  • or – логическое "или";
  • not – логическое "не";
  • круглые скобки ( и ) для указания порядка и удобства чтения кода.

Их использование аналогично обычным логическим операциям. Например, and вернет true только если образцы справа и слева от него будут успешно сопоставлены.

Рассмотрим несколько примеров:

public record Point(int X, int Y);
…
// Проверка на null. Аналог "point != null".
if (point is not null) { … }

// X не должен быть равен 0 или 1.
bool isValidX = point is { X: not 0 and not 1 };

// Y должен быть равен -1 или 1.
bool isValidY = point is { Y: -1 or 1 };

// Или X или Y должны быть равны 0.
bool isOnAxis = point is { X: 0 } or { Y: 0 };

// X и Y не должны быть равны ни 0.
bool isNotOnAxis = point is { X: not 0, Y: not 0 };

// X и Y должны быть внутри заданного для них диапазона.
bool isInside = point is { X: >= 0 and <= 10, Y: >= 0 and <= 5 };

Стоит отметить, что новые логические образцы и образцы соотношения позволяют избегать использования дополнительного условия where  в выражении switch. В результате код получается более компактный и удобный для чтения:

var index = point switch {
    // Синтаксис C# 8
    (var x, var y) when 0 <= x && x <= 10 && 0 <= y && y <= 5 => FindIndex(point),
    // Синтаксис C# 9
    { X: >= 0 and <= 10, Y: >= 0 and <= 5 } => FindIndex(point),
    _ => -1
};

Особенности логических образцов

and:

  • Не может использоваться с двумя образцами типа. Исключение – использование c типами интерфейсов.
obj is Point and Circle // Ошибка
obj is IPoint and ICircle // OK

or:

  • При использовании для двух образцов типа исходное значение не может быть присвоено в новую переменную.
obj is Point or Circle shape // Ошибка
obj is Point or Circle // OK
  • Не может использоваться в одном образце для двух свойств.
obj is { X: 0 or Y: 0 } // Ошибка
obj is { X: 0 } or { Y: 0 } // OK
  • Может быть использован внутри другого образца для перебора значений:
obj is { X: 1 or 2 } // OK

Комментарии (2) -

Хочу отметить, что не все иногда понимают разницу между a.X == 1 и a is { X: 1}.

endjin.com/.../dotnet-csharp-9-patterns-mechanism-over-intent

Хороший комментарий. a is {X: 1} включает в себя проверку на null и не выбросит исключение, в отличии от a.X == 1.  

Добавить комментарий