.NET 4 中的模式匹配

  • 2009-05-30
  • 本文字数:3531 字

    阅读完需:约 12 分钟

case 语句可以看作是 if/else 语法的特别版。他们的功能和作用是一样的,但有时 case 语句会令代码看起来更加清爽。考虑下面的 C#和 VB 示例。

<span>double </span>CaclRateByDate(<span>DayOfWeek </span>day)
 {
     <span>if </span>(day == <span>DayOfWeek</span>.Monday)
     {
         <span>return </span>.42;
     }
     <span>else if </span>(day == <span>DayOfWeek</span>.Tuesday)
     {
         <span>return </span>.67;
     }
     <span>else if </span>(day == <span>DayOfWeek</span>.Wednesday)
     {
         <span>return </span>.56;
     }
     <span>else if </span>(day == <span>DayOfWeek</span>.Thursday)
     {
         <span>return </span>.34;
     }
     <span>else if </span>(day == <span>DayOfWeek</span>.Friday)
     {
         <span>return </span>.78;
     }
     <span>else if </span>(day == <span>DayOfWeek</span>.Saturday)
     {
         <span>return </span>.92;
     }
     <span>else if </span>(day == <span>DayOfWeek</span>.Sunday)
     {
         <span>return </span>.18;
     }
     <span>throw new </span><span>ArgumentOutOfRangeException</span>(<span>"Unexpected enum value"</span>);
 }
<span>Function </span>CaclRateByDate(<span>ByVal </span>day <span>As </span>DayOfWeek) <span>As Double
   If </span>day = Monday <span>Then
       Return </span>0.42
   <span>ElseIf </span>day = Tuesday <span>Then
       Return </span>0.67
   <span>ElseIf </span>day = Wednesday <span>Then
       Return </span>0.56
   <span>ElseIf </span>day = Thursday <span>Then
       Return </span>0.34
   <span>ElseIf </span>day = Friday <span>Then
       Return </span>0.78
   <span>ElseIf </span>day = Saturday <span>Then
       Return </span>0.92
   <span>ElseIf </span>day = Sunday <span>Then
       Return </span>0.18
   <span>Else
       Throw New </span>ArgumentOutOfRangeException(<span>"Unexpected enum value"</span>)
   <span>End If
End Function</span>

开发者需要一遍又一遍地编写“ElseIf day =”或“else if (day ==”这种语句,但却并没有增加任何信息。这种语句简直就是一种折磨,不停地分散开发者的注意力,我指的是 DayOfWeek 和返回值。

在 VB 和 C#中,我们可以通过 case 语句进行简化。

<span>double </span>CaclRateByDate2(<span>DayOfWeek </span>day)
{
   <span>switch </span>(day)
   {
       <span>case </span><span>DayOfWeek</span>.Monday:
           <span>return </span>.42;
       <span>case </span><span>DayOfWeek</span>.Tuesday:
           <span>return </span>.67;
       <span>case </span><span>DayOfWeek</span>.Wednesday:
           <span>return </span>.56;
       <span>case </span><span>DayOfWeek</span>.Thursday:
           <span>return </span>.34;
       <span>case </span><span>DayOfWeek</span>.Friday:
           <span>return </span>.78;
       <span>case </span><span>DayOfWeek</span>.Saturday:
           <span>return </span>.92;
       <span>case </span><span>DayOfWeek</span>.Sunday:
           <span>return </span>.18;
       <span>default</span>:
           <span>throw new </span><span>ArgumentOutOfRangeException</span>(<span>"Unexpected enum value"</span>);
   }
}
<span>Function </span>CalcRateByDate2(<span>ByVal </span>day <span>As </span>DayOfWeek) <span>As Double
   Select Case </span>day
       <span>Case </span>Monday
           <span>Return </span>0.42
       <span>Case </span>Tuesday
           <span>Return </span>0.67
       <span>Case </span>Wednesday
           <span>Return </span>0.56
       <span>Case </span>Thursday
           <span>Return </span>0.34
       <span>Case </span>Friday
           <span>Return </span>0.78
       <span>Case </span>Saturday
           <span>Return </span>0.92
       <span>Case </span>Sunday
           <span>Return </span>0.18
       <span>Case Else
           Throw New </span>ArgumentOutOfRangeException(<span>"Unexpected enum value"</span>)
   <span>End Select
End Function</span>

即便如此还是有不少的重复代码。为什么总是不断地说需要一个返回值呢?像下面这样写岂不更好?

<span>double </span>CaclRateByDate2(<span>DayOfWeek </span>day)
{
   <span>return switch </span>(day)
   {
       <span>DayOfWeek</span>.Monday: .42;
       <span>DayOfWeek</span>.Tuesday: .67;
       <span>DayOfWeek</span>.Wednesday: .56;
       <span>DayOfWeek</span>.Thursday: .34;
       <span>DayOfWeek</span>.Friday: .78;
       <span>DayOfWeek</span>.Saturday: .92;
       <span>DayOfWeek</span>.Sunday: .18;
       <span>default</span>:
           <span>throw new </span><span>ArgumentOutOfRangeException</span>(<span>"Unexpected enum value"</span>);
   }
}
<span>Function </span>CalcRateByDate2(<span>ByVal </span>day <span>As </span>DayOfWeek) <span>As Double
   Return Select Case </span>day
       Monday: 0.42
       Tuesday: 0.67
       Wednesday: 0.56
       Thursday: 0.34
       Friday: 0.78
       Saturday: 0.92
       Sunday: 0.18
       <span>Case Else
           Throw New </span>ArgumentOutOfRangeException(<span>"Unexpected enum value"</span>)
   <span>End Select
End Function</span>

在消除了那些不必要的重复后,你会发现 C#和 VB 代码看起来是如此的接近。剩下的代码就是寻找的模式以及与模式所匹配的结果了。这就是众所周知的模式匹配

遗憾的是,在C# 4 和VB 10 中并没有提供该特性,但却有一门新语言提供了对模式匹配的支持。看看下面这个由 Mathew Podwysocki 编写的 F#示例(需要说明的是,在下面这些示例中都创建了相应的函数)。

<span>let</span> calcRateByDay2 (day:System.DayOfWeek) = 
 <span>match</span> day <span>with </span>
 | System.DayOfWeek.Monday -> 0.42 
 | System.DayOfWeek.Tuesday -> 0.67 
 | System.DayOfWeek.Wednesday -> 0.56 
 | System.DayOfWeek.Thursday -> 0.34 
 | System.DayOfWeek.Friday -> 0.78 
 | System.DayOfWeek.Saturday -> 0.92 
 | System.DayOfWeek.Sunday -> 0.18 
 | _ -> failwith "Unexpected enum value"

接下来 Mathew 又介绍了同时检查多个参数的方式。下面这个示例将下划线当作通配符。

<span>let</span> allowUrl url port =
 <span>match</span> (url, port) <span>with</span>
 | "http://www.microsoft.com/", 80 -> true
 | "http://example.com/", 8888 -> true
 | _, 80 -> true
 | _ -> false

遗憾的是,F#的语法并不简洁。如果想要操纵某个值,那就不得不通过名称或占位符来指定了。

<span>let</span> calcRateByDay3 (day:System.DayOfWeek) = 
 <span>match</span> day <span>with </span>
 | x when x >= System.DayOfWeek.Monday && x <= System.DayOfWeek.Friday -> 0.42
 | System.DayOfWeek.Saturday -> 0.92 
 | System.DayOfWeek.Sunday -> 0.18 
 | _ -> failwith "Unexpected enum value"

<span>let</span> calcRateByDay3 (day:System.DayOfWeek) = 
 <span>match</span> day <span>with </span>
 | _ when day >= System.DayOfWeek.Monday && day <= System.DayOfWeek.Friday -> 0.42
 | System.DayOfWeek.Saturday -> 0.92 
 | System.DayOfWeek.Sunday -> 0.18 
 | _ -> failwith "Unexpected enum value"

下面的代码用 VB 的 case 语句实现同样的功能。

<span>Function </span>CaclRateByDate3(<span>ByVal </span>day <span>As </span>DayOfWeek) <span>As Double
   Select Case </span>day
       <span>Case </span>Monday <span>To </span>Friday : <span>Return </span>0.42
       <span>Case </span>Saturday : <span>Return </span>0.92
       <span>Case </span>Sunday : <span>Return </span>0.18
       <span>Case Else
           Throw New </span>ArgumentOutOfRangeException(<span>"Unexpected enum value"</span>)
   <span>End Select
End Function</span>

如你所见,.NET 平台上的每种语言都有自己的一些语法优势,可以将他们应用到其他语言上而无需改变语言的核心。

查看英文原文: Pattern Matching in .NET 4