C#8.0中的模式匹配是什么?

是我还是函数式编程(FP)似乎最近很流行? 诸如Haskell,Elixir和F#之类的FP语言比以往任何时候都更强大,并且大型框架尝试采用一种更具功能性的方法。 甚至C#都尝试在语言中包含功能构造。

实际上,最新版本的C#(8.0)中最大的功能之一就是模式匹配。 模式匹配在C#中并不是真正的新事物。 到目前为止,这是非常基本的,并且感觉像是二等公民。 在最新版本的C#中,我们现在对此提供了全面的支持。

让我们潜入吧!

模式匹配是通过使用switch运算符实现的。 请稍等...该语言是否已不存在switch ? 是的,这使得采用此新功能非常混乱。

即使switch运算符的关键字,语法和行为相似,新功能也会显着不同。 最大的区别之一是新switch是一个表达式,旧switch是一个语句。 您不再有case和break语句。

表达与陈述

在我们继续之前的快速注释。 对于大多数人来说,语句和表达式之间的区别非常不清楚。 这是到目前为止我找到的最好的定义。

表达式 :计算时返回值的东西
语句 :执行某件事的一行代码

例如
声明

if (a == null )
{
    someCode...
}
else
{
    someCode2...
}

表达

a == null ? value1 : value2;

话虽如此

话虽如此,让我们探讨这两个switch操作符之间的异同。

旧开关

句法

switch ( value )
{
    case 1 : something...
        break ;
    case 2 : something2...
    case 3 : something3...
        break ;
    default :
        somethingDefault....
        break ;
}

旧的开关试图找到下一条要分支的指令。 然后,它将依次执行每一行,直到命中break语句为止。 它的行为就像goto语句,通常被认为是不好的做法。 该运算符仅运行代码,不返回任何内容,因此将其视为语句。

新开关

句法

var a = value1 switch
{
    matchingExpression => expression
    matchingExpression2 => expression
    ...
}

另一方面,新开关必须返回一个值。 实际上,每个可能的匹配都必须返回相同类型的值。 匹配表达式可以使用模式来匹配或不匹配。 因此,名称模式匹配。

模式的力量

匹配模式不仅在条件为true时匹配,还将对象转换为匹配的类型并将其自动分配给变量。 从那里,您可以匹配对象属性,甚至可以对那些属性评估布尔表达式。

没有比这更好的例子了

让我们探索一个具有许多业务规则的TollService应用程序。 我们将看到模式匹配如何使代码清晰易读。

V1-类型匹配

public decimal CalculateToll ( object vehicle )
{
    // Price per vehicle type
    // ==========
    // Car -> 2$
    // Taxi -> 3.50$
    // Bus -> 5$
    // Truck -> 10$
    return vehicle switch
    {
        Car c => 2.00 m,
        Taxi t => 3.50 m,
        Bus b => 5.00 m,
        DeliveryTruck t => 10.00 m,
        { } => throw new ArgumentException( "Not a known vehicle type" , nameof (vehicle)),
        null => throw new ArgumentNullException( nameof (vehicle))
    };
}

V2-属性匹配

您可以使用{}匹配对象的特定值

public decimal CalculateToll ( object vehicle )
{
    // Price considering occupancy
    // ===========
    // Car and taxi
    //   No passengers -> +0.50 $
    //   2 passengers -> -0.50 $
    //   3 or more passengers -> -1$
    return vehicle switch
    {
        Car { Passengers: 0 } => 2.00 m + 0.50 m,
        Car { Passengers: 1 } => 2.0 m,
        Car { Passengers: 2 } => 2.0 m - 0.50 m,
        Car _ => 2.00 m - 1.0 m,

        Taxi { Fares: 0 } => 3.50 m + 1.00 m,
        Taxi { Fares: 1 } => 3.50 m,
        Taxi { Fares: 2 } => 3.50 m - 0.50 m,
        Taxi _ => 3.50 m - 1.00 m,

        Bus b => 5.00 m,
        DeliveryTruck t => 10.00 m,
        { } => throw new ArgumentException( "Not a known vehicle type" , nameof (vehicle)),
        null => throw new ArgumentNullException( nameof (vehicle))
    };
}

V3-条件布尔表达式

您可以使用when关键字定义将在对象上评估的布尔表达式。 您可能已经熟悉when关键字。 它是先前在C#6.0中的catch语句中引入的,用于评估异常属性的条件。 您可以想象,它在这里的工作方式完全相同,因此您不会迷路。

public decimal CalculateToll ( object vehicle )
{
    // Price considering occupancy
    // ===========
    // Bus
    //   Less than 50% full -> +2$
    //   More than 90% full -> -1$
    return vehicle switch
    {
        // car and taxi hidden here to make it easier to read
        // ...
        Bus b when ( double )b.Riders / ( double )b.Capacity < 0.50 => 5.00 m + 2.00 m,
        Bus b when ( double )b.Riders / ( double )b.Capacity > 0.90 => 5.00 m - 1.00 m,
        Bus _ => 5.00 m,

        DeliveryTruck t => 10.00 m,
        { } => throw new ArgumentException( "Not a known vehicle type" , nameof (vehicle)),
        null => throw new ArgumentNullException( nameof (vehicle))
    };
}

V4-嵌套

switch运算符必须返回一个表达式,并且由于它们本身就是表达式,这意味着我们可以嵌套它们以使我们的代码更易于阅读。

public decimal CalculateToll ( object vehicle )
{
    return vehicle switch
    {
        Car c => c.Passengers switch
        {
            0 => 2.00 m + 0.5 m,
            1 => 2.0 m,
            2 => 2.0 m - 0.5 m,
            _ => 2.00 m - 1.0 m
        },

        Taxi t => t.Fares switch
        {
            0 => 3.50 m + 1.00 m,
            1 => 3.50 m,
            2 => 3.50 m - 0.50 m,
            _ => 3.50 m - 1.00 m
        },

        // Bus and truck hidden here to make it easier to read
        // ...
    };
}

特殊匹配表达式

您可能已经注意到在前面的示例中,我们一直在使用一些特殊的匹配表达式{}null_

  • {} :非null,但类型未知
  • null :空值
  • _ :绝对匹配任何通配符,但不捕获新变量中的值

Gotcha

匹配表达式从上到下进行评估。 始终将限制性最强的匹配表达式放在首位,并尝试以“特殊”匹配表达式结束。
例如,如果您使用 _ 作为第一行,则它将始终在此表达式上匹配,并且不会评估其他任何内容。

结束语

模式匹配功能非常强大,可用于简化复杂的业务逻辑,同时使其更易于阅读。 请继续关注本系列的更多博客文章!

所有代码示例都可以在github上找到

参考文献

先前发布在https://blog.miguelbernard.com/pattern-matching-in-csharp/

From: https://hackernoon.com/whats-pattern-matching-in-c-80-6l7h3ygm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值