Pattern Matching(模式匹配新用法,C#7.0)–C#

  • Post author:
  • Post category:其他


今天突然看到C#7.0的Pattern Matching有一些新的用法,特意了解下。


https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching

Pattern Matching 分离数据和代码,与ood模式把数据和方法封装在一起不同。

1, is类型模式表达式

以下是一个shape的例子,这里面没有定义什么的shape抽象基类以及特定的子类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public class Square
    {
        public double Side { get; }

        public Square(double side)
        {
            Side = side;
        }
    }
    public class Circle
    {
        public double Radius { get; }

        public Circle(double radius)
        {
            Radius = radius;
        }
    }
    public struct Rectangle
    {
        public double Length { get; }
        public double Height { get; }

        public Rectangle(double length, double height)
        {
            Length = length;
            Height = height;
        }
    }
    public class Triangle
    {
        public double Base { get; }
        public double Height { get; }

        public Triangle(double @base, double height)
        {
            Base = @base;
            Height = height;
        }
    }
}

在C#7.0之前,比较典型的是使用if判断,然后进入各分支执行相应的逻辑。

 /* it is a classic expression of the type pattern before C#7.0:
         * testing a variable to determine its type
         * and taking a different action based on that type
         */
        public static double ComputeArea(object shape)
        {
            if (shape is Square)
            {
                var s = (Square)shape;
                return s.Side * s.Side;
            }
            else if (shape is Circle)
            {
                var c = (Circle)shape;
                return c.Radius * c.Radius * Math.PI;
            }
            else if(shape is Rectangle)
            {
                var r = (Rectangle) shape;
                return r.Height * r.Length;
            }
            // elided
            throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: shape.GetType().ToString());
        }

在C#7.0之后,使用is表达式

/*the is expression both test the variable
         *and assigns it to a new variable of the proper type
         */
        public static double ComputeAreaModernIs(object shape)
        {
            if (shape is Square s)
                return s.Side * s.Side;
            else if (shape is Circle c)
                return c.Radius * c.Radius * Math.PI;
            else if (shape is Rectangle r)
                return r.Height * r.Length;
            // elided
            throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: nameof(shape));
        }

在上面的代码中,is表达式首先判断变量的合法性,在变量合法的前提下将会新建一个恰当类型的变量。

2,switch的模式匹配

传统的c风格的switch,对参数有严格的限制,仅允许常量,示例如下:

/*
         *it only support the constant pattern
         */

        public static string GenerateMessage(params string[] parts)
        {
            switch (parts.Length)
            {
                case 0:
                    return "No elements to the input";
                case 1:
                    return $"One element: {parts[0]}";
                case 2:
                    return $"Two elements: {parts[0]}, {parts[1]}";
                default:
                    return $"Many elements. Too many to write";
            }
        }

而新的switch的模式匹配已经删除这些限制,代码如下:

/*
         *
         */
        public static double ComputeAreaModernSwitch(object shape)
        {
            switch (shape)
            {
                case Square s:
                    return s.Side * s.Side;
                case Circle c:
                    return c.Radius * c.Radius * Math.PI;
                case Rectangle r:
                    return r.Height * r.Length;
                default:
                    throw new ArgumentException(
                        message: "shape is not a recognized shape",
                        paramName: nameof(shape));
            }
        }

3,switch表达式是when条件语句

when语句可以在表达式中作为判断条件,代码如下:

/*
         *use when clause in case expression,
         */
        public static double ComputeArea_Version3(object shape)
        {
            switch (shape)
            {
            case Square s when s.Side == 0:
            case Circle c when c.Radius == 0:
            case Triangle t when t.Base == 0 || t.Height == 0:
            case Rectangle r when r.Length == 0 || r.Height == 0:
                return 0;

            case Square s:
                return s.Side * s.Side;
            case Circle c:
                return c.Radius * c.Radius * Math.PI;
            case Triangle t:
                return t.Base * t.Height / 2;
            case Rectangle r:
                return r.Length * r.Height;
            case null:
                throw new ArgumentNullException(paramName: nameof(shape), message: "Shape must not be null");
            default:
                throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: nameof(shape));
            }
        }

4,var关键字在case表达式的使用

static object CreateShape(string shapeDescription)
{
    switch (shapeDescription)
    {
        case "circle":
            return new Circle(2);

        case "square":
            return new Square(4);
        
        case "large-circle":
            return new Circle(12);

        case var o when (o?.Trim().Length ?? 0) == 0:
            // white space
            return null;
        default:
            return "invalid shape description";
    }            
}