Паттерн свойств позволяет сравнивать со значениями определенных свойств объекта. Например, пусть у нас будет следующий класс:
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"); } }