Паттерн свойств

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

Паттерн свойств позволяет сравнивать со значениями определенных свойств объекта. Например, пусть у нас будет следующий класс:

class Person
{
    public string Name { get; set; } = "";        // имя пользователя
    public string Status { get; set; } = "";     // статус пользователя
    public string Language { get; set; } = "";   // язык пользователя
}

Например, в зависимости от языка пользователя выведем ему определенное сообщение, применив паттерн свойств:

Person tom = new Person { Language = "english", Status = "user", Name = "Tom" };
Person pierre = new Person { Language = "french", Status = "user", Name = "Pierre" };

SayHello(tom);		// Hello
SayHello(pierre);	// Salut

void SayHello(Person person)
{
    if(person is Person { Language: "french" })
    {
        Console.WriteLine("Salut");
    }
    else
    {
        Console.WriteLine("Hello");
    }
}

Здесь метод SayHello в качестве параметра принимает объект Person и сопоставляет его с некоторым паттерном. В качестве паттерна выступает выражение Person { Language: "french" }. То есть параметр person должен представлять объект Person, у которого значение свойства Language равно "french".

При этом можно задействовать набор свойств. Например, добавим проверку по свойству Status:

Person tom = new Person { Language = "english", Status = "user", Name = "Tom" };
Person pierre = new Person { Language = "french", Status = "user", Name = "Pierre" };
Person admin = new Person { Language = "english", Status = "admin", Name = "Admin" };

SayHello(admin);    // Hello, admin
SayHello(tom);      // Hello
SayHello(pierre);   // Salut

void SayHello(Person person)
{
    if(person is Person { Language: "english", Status: "admin" })
    {
        Console.WriteLine("Hello, admin");
    }
    else if (person is Person { Language: "french"})
    {
        Console.WriteLine("Salut");
    }
    else
    {
        Console.WriteLine("Hello");
    }
}

Теперь выражение if проверяет, соответствует ли параметр person объекту Person, у которого свойства Language и Status имеют определенные значения.

Подобным образом можно применять паттерн свойств в конструкции switch:

string GetMessage(Person? p) => p switch
{
    { Language: "english" } => "Hello!",
    { Language: "german", Status: "admin" } => "Hallo, admin!",
    { Language: "french" } => "Salut!",
    { } => "undefined",
    null => "null"       // если Person p = null
};

Паттерны свойств предполагают использование фигурных скобок, внутри которых указываются свойства и через двоеточие их значение {свойство: значение}. И со значением свойства в фигурных скобках сравнивается свойство передаваемого объекта. При этом в фигурных скобках мы можем указать несколько свойств и их значений { Language: "german", Status: "admin" } - тогда свойства передаваемого объекта должны соответствовать всем этим значениям.

Можно оставить пустые фигурные скобки, как в последнем случае { } => "undefined!" - передаваемый объект будет соответствовать пустым фигурным скобкам, если он не соответствует всем предыдущим значениям, или например, если его свойства не указаны или имеют значение null.

То есть в данном случае, если у объекта Person p выполняется равенство Language = "english", будет возвращаться строка "Hello!".

Если у объекта Person p одновременно выполняются два равенства Language = "german" и Role="admin", будет возвращаться строка "Hallo, admin!".

Если у объекта Person p выполняется равенство Language = "french", будет возвращаться строка "Salut!".

Если объект Person будет сопоставляться с пустыми фигурными скобками {}, и будет возвращаться строка "undefined".

Последняя проверка проверяет значение на null.

Применение:

Person pierre = new Person { Language = "french", Status = "user", Name = "Pierre" };
string message = GetMessage(pierre);
Console.WriteLine(message);     // Salut!

Person tomas = new Person { Language = "german", Status = "admin", Name = "Tomas" };
Console.WriteLine(GetMessage(tomas));     // Hallo, admin!

Person pablo = new Person { Language = "spanish", Status = "user", Name = "Pablo" };
Console.WriteLine(GetMessage(pablo));     // undefined

Console.WriteLine(GetMessage(null));     // null

Кроме того, мы можем определять в паттерных свойств переменные, передавать этим переменным значения объекта и использовать при возвращении значения:

string GetMessage(Person? p) => p switch
{
	{ Language: "german", Status: "admin" } => "Hallo, admin!",
	{ Language: "french", Name: var name } => $"Salut, {name}!",
	{ Language: var lang} => $"Unknown language: {lang}",
	null => "null"
};

Так, подвыражение Name: var name говорит, что надо передать в переменную name значение свойства Name. Затем ее можно применить при генерации выходного значения: => $"Salut, {name}!"

Применение:

Person pierre = new Person { Language = "french", Status = "user", Name = "Pierre" };
string message = GetMessage(pierre);
Console.WriteLine(message);             // Salut, Pierre!

Person tomas = new Person { Language = "german", Status = "admin", Name = "Tomas" };
Console.WriteLine(GetMessage(tomas));     // Hallo, admin!

Person pablo = new Person { Language = "spanish", Status = "user", Name = "Pablo" };
Console.WriteLine(GetMessage(pablo));     // Unknown language: spanish

Person? bob = null;
Console.WriteLine(GetMessage(bob));         // null

Стоит отметить, что начиная с версии C# 10 было упрощено сопоставление со свойствами вложенных объектов. Допустим, у нас есть следующие классы:

class Employee
{
    public string Name { get;}
    public Company Company { get; set; }
    public Employee(string name, Company company)
    {
        Name = name;
        Company = company;
    }

}
class Company
{
    public string Title { get;}
    public Company(string title) => Title = title;
}

Класс Company определяет свойство Title, которое хранит название компании. Класс Employee определяет сотрудника компании и в свойстве Company хранит компанию. Применим паттерн свойств на основе свойств вложенного объекта Company:

var microsoft = new Company("Microsoft");
var google = new Company("Google");
var tom = new Employee("Tom", microsoft);
var bob = new Employee("Bob", google);

PrintCompany(tom);    // 
PrintCompany(bob);    // 

void PrintCompany(Employee employee)
{
    if (employee is Employee { Company:{Title: "Microsoft" } })
    {
        Console.WriteLine($"{employee.Name} works in Microsoft");
    }
    else
    {
        Console.WriteLine($"{employee.Name} works someware");
    }
}

В методе PrintCompany объект employee сопоставляется с паттерном Employee { Company:{Title: "Microsoft" } }. То есть сотрудник компании должен представлять объект Employee, у которого название компании равно "Microsoft"

Однако мы также можем сократить данный паттерн следующим образом:

void PrintCompany(Employee employee)
{
    if (employee is Employee { Company.Title: "Microsoft" })
    {
        Console.WriteLine($"{employee.Name} works in Microsoft");
    }
    else
    {
        Console.WriteLine($"{employee.Name} works someware");
    }
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850