今天突然看到C#7.0的Pattern Matching有一些新的用法,特意了解下。
https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching
Pattern Matching 分离数据和代码,与ood模式把数据和方法封装在一起不同。
以下是一个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表达式首先判断变量的合法性,在变量合法的前提下将会新建一个恰当类型的变量。
传统的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));
}
}
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));
}
}
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";
}
}