总结一下C#的模式匹配功能

从C#7开始推出模式匹配以来,每次C#版本升级都会对模式匹配进行升级,到最新的C#9,又更新了好几个关于模式匹配的新特性(新玩法),可见模式匹配这一特性在C#语言生态中的重要地位。模式匹配到C#9为止,目前有这么多种用法,在这里总结归纳一下,以后才能玩的更溜。

C#7里的第一代模式匹配

一句话:C# 7.0 通过使用 is 表达式和 switch 语句引入了类型模式和常量模式的语法。

  • is表达式的用法
  • 以下对引用类型和值类型进行测试,可以将成功结果分配给类型正确的新变量
public async static Task Main()
{
    var o = new object();
    var s = new Student()
    {
        Name = "luminqiang",
        Age = 25
    };

    if (o is Student student)
    {
        Console.WriteLine(student.Name + student.Age);
    }

    if (s is Student student1)
    {
        var dseds = s.Equals(student1);
        Console.WriteLine(student1.Name + student1.Age);
    }

    int number_one = 10;
    if (number_one is int number_two)
    {
        number_two += 10;
    }
}

    public class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
  • switch 匹配表达式的用法
  • switch语句不再限制为常量值如数字类型和字符串,还可以使用其他自定义类型
case 0: 是常见的常量模式。
case IEnumerable<int> childSequence: 是一种类型模式。
case int n when n > 0: 是具有附加 when 条件的类型模式。
case null: 是 null 模式。
default: 是常见的默认事例。
var student = new Student
{
    Age = 25,
    Name = "luminqiang"
};

switch (student)
{         
    case Student s when s.Name == "lumiqiang":
        Console.WriteLine("这个是学霸");
        break;
    case object o when o == null:
        Console.WriteLine("这个是谁不知道");
        break;
    default:
        throw new ArgumentException("不合法的类型");
}

C#8里的第二代模式匹配

public enum Rainbow
{
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Indigo,
    Violet
}

这里有几个语法改进:

  • 变量位于 switch 关键字之前。 不同的顺序使得在视觉上可以很轻松地区分 switch 表达式和 switch 语句。
  • 将 case 和 : 元素替换为 =>。 它更简洁,更直观。
  • 将 default 事例替换为 _ 弃元。
  • 正文是表达式,不是语句。
public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
    Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
    Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
    Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
    Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
    Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
    Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
    Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
    _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};

下面是经典的常规写法,我目前还是更喜欢这种经典的写法,反而我解决的这种经典写法更直观,哈哈,虽然代码多了几行,新写法可能需要时间来适应一下

public static RGBColor FromRainbowClassic(Rainbow colorBand)
{
    switch (colorBand)
    {
        case Rainbow.Red:
            return new RGBColor(0xFF, 0x00, 0x00);
        case Rainbow.Orange:
            return new RGBColor(0xFF, 0x7F, 0x00);
        case Rainbow.Yellow:
            return new RGBColor(0xFF, 0xFF, 0x00);
        case Rainbow.Green:
            return new RGBColor(0x00, 0xFF, 0x00);
        case Rainbow.Blue:
            return new RGBColor(0x00, 0x00, 0xFF);
        case Rainbow.Indigo:
            return new RGBColor(0x4B, 0x00, 0x82);
        case Rainbow.Violet:
            return new RGBColor(0x94, 0x00, 0xD3);
        default:
            throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand));
    };
}
  • 属性模式:可以匹配所检查的对象的属性
public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.075M,
        { State: "MI" } => salePrice * 0.05M,
        // other cases removed for brevity...
        _ => 0M
    };
  • 元组模式:使用元组模式,可根据表示为元组的多个值进行切换
public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        ("paper", "rock") => "paper covers rock. Paper wins.",
        ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
        ("scissors", "rock") => "scissors is broken by rock. Rock wins.",
        ("scissors", "paper") => "scissors cuts paper. Scissors wins.",
        (_, _) => "tie"
    };
  • 位置模式:某些类型包含 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);
}
public enum Quadrant
{
    Unknown,
    Origin,
    One,
    Two,
    Three,
    Four,
    OnBorder
}
static Quadrant GetQuadrant(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
};

C#9里的第三代模式匹配

主要是进行了一些改进

  • 类型模式要求在变量是一种类型时匹配
  • 带圆括号的模式强制或强调模式组合的优先级
  • 联合 and 模式要求两个模式都匹配
  • 析取 or 模式要求任一模式匹配
  • 求反 not 模式要求模式不匹配
  • 关系模式要求输入小于、大于、小于等于或大于等于给定常数。
public static bool IsLetter(this char c) =>
    c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';

使用可选的括号来明确 and 的优先级高于 or:

public static bool IsLetterOrSeparator(this char c) =>
    c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';

最常见的用途之一是用于 NULL 检查的新语法:

if (e is not null)
{
    // ...
}

总结:目前来说,并不是所有的模式匹配语法都用的上,但是以后可以使用模式匹配改进的地方还是值得一试的

posted @ 2021-01-07 13:50  Yeah的第七章  阅读(557)  评论(0编辑  收藏  举报