冠军

导航

C# 7.0 新特性:模式匹配 ( pattern matching)

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

 

 

其它资源:

 

posted on 2020-03-28 17:53  冠军  阅读(1995)  评论(0编辑  收藏  举报