Паттерн типов


Pattern matching фактически выполняет сопоставление некоторого значения с некоторым шаблоном. И если сопоставление прошло успешно, то выполняются определенные действия. Язык C# позволяет выполнять различные типы сопоставлений.

Паттерн типов или type pattern позволяет проверить некоторое значение на соответствие некоторому типу:

значение is тип переменная_типа
Например, у нас есть следующие классы:

class Employee
{
public virtual void Work() => Console.WriteLine("Employee works");
}

class Manager : Employee
{
public override void Work() => Console.WriteLine("Manager works");
public bool IsOnVacation { get; set; }
}
Класс Employee представляет работника, а класс Manager - менеджера. Оба класса реализуют метод Work. Кроме того, класс Manager определяет свойство IsOnVacation.

С помощью паттерна типов проверим, представляет ли объект Employee класс Manager:

Employee tom = new Manager();
UseEmployee(tom); // Manager works

void UseEmployee(Employee emp)
{
if (emp is Manager manager && manager.IsOnVacation == false)
{
manager.Work();
}
else
{
Console.WriteLine("Преобразование не допустимо");
}
}
Здесь в методе UseEmployee значение emp сопоставляется с типом Manager. То есть в данном случае в качестве шаблона выступает тип Manager. Если сопоставление прошло успешно (то есть значение emp представляет тип Manager), в переменной manager оказывается объект emp. И далее мы можем вызвать у него методы и свойства.

Также мы можем использовать constant pattern - сопоставление с некоторой константой:

var message = "hello";

// проверяем, соответствует ли значение message строке "hello"
if (message is "hello")
{
Console.WriteLine("hello");
}
Подобным образом, например, можно проверить значение на null:

Employee? bob = new Employee();
Employee? tom = null;

UseEmployee(bob);
UseEmployee(tom);

void UseEmployee(Employee? emp)
{
if (emp is not null)
emp.Work();
}
Кроме конструкции if сопоставление паттернов может применяться в конструкции switch:

Employee bob = new Employee();
Employee tom = new Manager();
UseEmployee(tom); // Manager works
UseEmployee(bob); // Object is not manager

void UseEmployee(Employee? emp)
{
switch (emp)
{
case Manager manager:
manager.Work();
break;
case null:
Console.WriteLine("Object is null");
break;
default:
Console.WriteLine("Object is not manager");
break;
}
}
С помощью выражения when можно вводить дополнительные условия в конструкцию case:

Employee bob = new Manager() { IsOnVacation = true };
Employee tom = new Manager() { IsOnVacation = false };
UseEmployee(tom); // Manager works
UseEmployee(bob); // Employee does not work

void UseEmployee(Employee? emp)
{
switch (emp)
{
case Manager manager when !manager.IsOnVacation:
manager.Work();
break;
case null:
Console.WriteLine("Employee is null");
break;
default:
Console.WriteLine("Employee does not work");
break;
}
}
В этом случае опять же преобразуем объект emp в объект типа Manager и в случае удачного преобразования смотрим на значение свойства IsOnVacation: если оно равно false, то выполняется данный блок case.