C# 7.0 新特性:模式匹配 ( pattern matching )
在 C# 中,is 是一个关键字,可以用来检查某个数据的类型是否为特定类型。这是一个表达式,返回类型为 boolean。
例如,我们可以检查某个实例是否为 Persion 类型
if (obj is Person) { // Do something if obj is a Person. }
在下面情况下,返回 true:
- 表达式的类型与 is 类型相符
- 表达式的类型为 is 类型的派生类型
- 表达式具有一个编译时类型, 它是 is 类型的基类,在运行时的值为 is 类型的派生类型
- 表达式实现了 is 类型的接口
支持模式匹配的 is
类型模式
在 C# 7.0 中,is 在原来的基础上,额外提供了类型转换的支持。可以在类型检查的基础上,直接支持类型转换。
在下面的示例中,我们希望检查对象的类型,如果为指定类型,我们要转型为特定的类型进行操作。
using System; public class Example { public static void Main() { Object o = new Person("Jane"); ShowValue(o); o = new Dog("Alaskan Malamute"); ShowValue(o); } public static void ShowValue(object o) { if (o is Person p) { Console.WriteLine(p.Name); } else if (o is Dog d) { Console.WriteLine(d.Breed); } } } public struct Person { public string Name { get; set; } public Person(string name) : this() { Name = name; } } public struct Dog { public string Breed { get; set; } public Dog(string breedName) : this() { Breed = breedName; } } // The example displays the following output: // Jane // Alaskan Malamute
等价的以前代码为:
if (o is Person) { Person p = (Person) o; Console.WriteLine(p.Name); } else if (o is Dog) { Dog d = (Dog) o; Console.WriteLine(d.Breed); }
常量匹配
is 后面还可以是常量,is 测试表达式的值是否为特定常量。在以前版本中,这需要使用 switch 来支持。
using System; public class Dice { Random rnd = new Random(); public Dice() { } public int Roll() { return rnd.Next(1, 7); } } class Program { static void Main(string[] args) { var d1 = new Dice(); ShowValue(d1); } private static void ShowValue(object o) { const int HIGH_ROLL = 6; if (o is Dice d && d.Roll() is HIGH_ROLL) Console.WriteLine($"The value is {HIGH_ROLL}!"); else Console.WriteLine($"The dice roll is not a {HIGH_ROLL}!"); } } // The example displays output like the following: // The value is 6!
var 匹配
如果 is 后面是 var,则永远为 true 。并把值赋予后面的变量。
例如,下面代码将 item 赋予了 obj。
if (item is var obj)
需要注意的是,即使被测试的值为 null,is 表达式还是为 true。此时,变量将被赋予 null。
支持模式匹配的 Switch
所有的 c# 版本都支持常量模式,在 C# 7.0 中,现在支持类型模式了。也就是说,在 case 后面还可以是一个用来检测的类型。
case type varname
相当于在这里使用了 is 。
从 C# 7.0 开始,您还可以在上面的表达式后面附加一个返回 boolean 的 when 条件,以进一步检查。使用它的常见场景就是当值为 null 时。
using System; public abstract class Shape { public abstract double Area { get; } public abstract double Circumference { get; } } public class Rectangle : Shape { public Rectangle(double length, double width) { Length = length; Width = width; } public double Length { get; set; } public double Width { get; set; } public override double Area { get { return Math.Round(Length * Width,2); } } public override double Circumference { get { return (Length + Width) * 2; } } } public class Square : Rectangle { public Square(double side) : base(side, side) { Side = side; } public double Side { get; set; } } public class Circle : Shape { public Circle(double radius) { Radius = radius; } public double Radius { get; set; } public override double Circumference { get { return 2 * Math.PI * Radius; } } public override double Area { get { return Math.PI * Math.Pow(Radius, 2); } } } public class Example { public static void Main() { Shape sh = null; Shape[] shapes = { new Square(10), new Rectangle(5, 7), sh, new Square(0), new Rectangle(8, 8), new Circle(3) }; foreach (var shape in shapes) ShowShapeInfo(shape); } private static void ShowShapeInfo(Shape sh) { switch (sh) { // Note that this code never evaluates to true. case Shape shape when shape == null: Console.WriteLine($"An uninitialized shape (shape == null)"); break; case null: Console.WriteLine($"An uninitialized shape"); break; case Shape shape when sh.Area == 0: Console.WriteLine($"The shape: {sh.GetType().Name} with no dimensions"); break; case Square sq when sh.Area > 0: Console.WriteLine("Information about square:"); Console.WriteLine($" Length of a side: {sq.Side}"); Console.WriteLine($" Area: {sq.Area}"); break; case Rectangle r when r.Length == r.Width && r.Area > 0: Console.WriteLine("Information about square rectangle:"); Console.WriteLine($" Length of a side: {r.Length}"); Console.WriteLine($" Area: {r.Area}"); break; case Rectangle r when sh.Area > 0: Console.WriteLine("Information about rectangle:"); Console.WriteLine($" Dimensions: {r.Length} x {r.Width}"); Console.WriteLine($" Area: {r.Area}"); break; case Shape shape when sh != null: Console.WriteLine($"A {sh.GetType().Name} shape"); break; default: Console.WriteLine($"The {nameof(sh)} variable does not represent a Shape."); break; } } } // The example displays the following output: // Information about square: // Length of a side: 10 // Area: 100 // Information about rectangle: // Dimensions: 5 x 7 // Area: 35 // An uninitialized shape // The shape: Square with no dimensions // Information about square rectangle: // Length of a side: 8 // Area: 64 // A Circle shape