В этой статье мы рассмотрим выражения Switch и сопоставление с образцом. Выражение Switch развивалось в течение нескольких версий, и в C # 8 оно значительно изменилось. В новом выражении switch значительно уменьшено количество повторяющихся ключевых слов case и break.

Давайте посмотрим как на традиционные, так и на новые выражения switch, чтобы понять изменения. Мы начнем с традиционного выражения следующим образом.


    private static RGBColor ExecuteOldSwitch(PrimaryColor color)  
    {  
       switch (color)  
       {  
          case PrimaryColor.Red:  
          return new RGBColor(0xFF, 0x00, 0x00); case PrimaryColor.Green:  
          return new RGBColor(0x00, 0xFF, 0x00); case PrimaryColor.Blue:  
          return new RGBColor(0x00, 0x00, 0xFF); default:  
          throw new ArgumentException(message: "invalid color", paramName: nameof(color));  
       };  
    }  

Теперь давайте посмотрим на современный оператор switch, представленный в C #, следующим образом.


    private static RGBColor ExecuteNewSwitch(PrimaryColor color) => color switch  
    {  
       PrimaryColor.Red => new RGBColor(0xFF, 0x00, 0x00), PrimaryColor.Green => new RGBColor(0x00, 0xFF, 0x00), PrimaryColor.Blue => new RGBColor(0x00, 0x00, 0xFF),  
       _ => throw new ArgumentException(message: "invalid color", paramName: nameof(color))  
    };  

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

  • Имя переменной ставится перед переключателем.
  • Элементы case и: заменяются на =>.
  • default был заменен на _ .
  • Параметры — это больше не утверждения, а выражения.

Из новых операторов switch появились некоторые дополнительные шаблоны, такие как шаблоны свойств, кортежей и позиций. Давайте посмотрим на них по очереди.

Шаблон свойств

Шаблон свойств помогает сравнивать свойства объекта.

Рассмотрим сайт расчета государственных налогов, который должен рассчитывать налог на основе адреса покупателя. В этом случае размер налога зависит от государственной собственности адреса. В следующем примере используется шаблон свойства для вычисления налога с адреса вместе с общей ценой.


    public static void ExecutePropertyPattern()  
    {  
       Address address = new Address{ State = "MN"}; Console.WriteLine($"Overall price (including tax) of  
       {address.State} is: {ComputeOverallPrice(address, 2.4M)}");  
    }  
    private static decimal ComputeOverallPrice(Address location, decimal price)  
    =>  
    location switch  
    {  
       { State: "MN" } => price + price * 0.78M,  
       { State: "MI" } => price + price * 0.06M,  
       { State: "WA" } => price + price * 0.07M,  
       _ => 0M  
    };  

Шаблон кортежа

Теперь давайте посмотрим на шаблон кортежа, он похож на шаблон свойств с той разницей, что для сравнения используются значения кортежа.

Давайте посмотрим на пример следующим образом.


    public static void ExecuteTuplePattern()  
    {  
       (string, string, string) counties = ("India", "Australia","England");  
      
       var team1 = counties.Item1.ToString();   
       var team2 = counties.Item3.ToString();  
       Console.WriteLine($"Result of the match between {team1} and {team2} is: {ReturnWinner(team1, team2)}");  
    }  
      
    private static string ReturnWinner(string team1, string team2)  
    => (team1, team2) switch  
    {  
       ("India", "Australia") => "Australia is covered by India. India wins.",  
       ("Australia", "England") => "Australia breaks England. Australia wins.",  
       ("India", "England") => "India covers England. India wins.",  
       (_, _) => "tie"  
    };  

Как вы можете видеть в приведенном выше коде, значение кортежа сравнивается, а выражение соответствующего значения кортежа возвращается вызывающей стороне. В приведенном выше примере результат будет отображаться как «Результат матча между Индией и Англией: Индия покрывает Англию. Индия побеждает».

Позиционный шаблон

Помимо свойств и кортежей, шаблоны могут совпадать даже по позициям. Сопоставление с позиционным образцом работает с помощью деконструктора, представленного в C # 7.

Деконструктор позволяет устанавливать свои свойства в дискретные переменные и позволяет компактно использовать позиционные шаблоны без необходимости называть свойства.

Давайте рассмотрим пример, чтобы лучше понять это. Для начала создадим класс Point, который использует метод deconstruct.


    public class Point  
    {  
       public int X { get; } public int Y { get; }  
       public Point(int x, int y) => (X, Y) = (x, y); public void Deconstruct(out int x, out int y)  
       {  
          (x, y) = (X, Y);  
       }  
    }  

Обратите внимание, что здесь имя метода Deconstruct требуется компилятору, и любое другое имя с таким же телом метода не будет работать.

Нам также необходимо иметь перечисление, чтобы упростить позиционное сравнение следующим образом.


    public enum Quadrant  
    {  
       Origin, One,   
       Two, Three, Four,  
       OnBorder, Unknown  
    }  

Теперь у нас есть логика для поиска шаблонов и метод для их вызова и печати на консоли.

public static void ExecutePositionalPattern()  
{  
   Point point = new Point(5,10);  
   Console.WriteLine($"Quadrant of point {point.X} and {point.Y} is: {FindQuadrant(point)}");  
}  
  
private static Quadrant FindQuadrant(Point point) => point switch  
{  
   (0, 0) => Quadrant.Origin,  
   var (x, y)  when    x   >    0   &&  y   >    0   =>   Quadrant.One,  
   var (x, y)  when    x   <    0   &&  y   >    0   =>   Quadrant.Two,  
   var (x, y)  when    x   <    0   &&  y   <    0   =>   Quadrant.Three,  
   var (x, y)  when    x   >    0   &&  y   <    0   =>   Quadrant.Four,  
   var (_, _) => Quadrant.OnBorder,  
   _ => Quadrant.Unknown  
}; 

Приведенный выше код будет выводиться как «Квадрант точек 5 и 10 равен: Один», поскольку пройденные точки 5 и 10 попадают в первый квадрант.

В приведенном выше коде шаблон отбрасывания (_) соответствует, когда либо x, либо y равно 0, но не оба. Важным моментом в выражении switch является то, что оно должно либо выдавать значение при совпадении вариантов, либо выдавать исключение, если ни один из вариантов не соответствует. Кроме того, компилятор выдает предупреждение, если вы не включили все возможные случаи в выражение переключения.

Примечание

Деконструкция — не лучший выбор, и ее следует использовать только для тех типов, где она имеет смысл и понятнее для интерпретации. Например, для класса Point было легко и интуитивно понятно предположить, что первое значение — X, а второе — Y, поэтому внимательно используйте позиционные шаблоны.

Резюме

В этой статье мы рассмотрели новый синтаксис переключателя и сравнили его с традиционным. Мы также поняли, как шаблоны (свойство, кортеж и позиционирование) можно использовать с переключателем, чтобы сделать его еще более мощным. Совершенно очевидно, что новый синтаксис переключателя более четкий и простой в использовании.