总结一下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)
{
// ...
}
总结:目前来说,并不是所有的模式匹配语法都用的上,但是以后可以使用模式匹配改进的地方还是值得一试的