Case statements can be thought of as specialized version of the if/else syntax. There is nothing you couldn’t already do with them, and yet there are times when they seem much clearer. Consider this example in C# and VB.
double CaclRateByDate(DayOfWeek day) { if (day == DayOfWeek.Monday) { return .42; } else if (day == DayOfWeek.Tuesday) { return .67; } else if (day == DayOfWeek.Wednesday) { return .56; } else if (day == DayOfWeek.Thursday) { return .34; } else if (day == DayOfWeek.Friday) { return .78; } else if (day == DayOfWeek.Saturday) { return .92; } else if (day == DayOfWeek.Sunday) { return .18; } throw new ArgumentOutOfRangeException("Unexpected enum value"); }
Function CaclRateByDate(ByVal day As DayOfWeek) As Double If day = Monday Then Return 0.42 ElseIf day = Tuesday Then Return 0.67 ElseIf day = Wednesday Then Return 0.56 ElseIf day = Thursday Then Return 0.34 ElseIf day = Friday Then Return 0.78 ElseIf day = Saturday Then Return 0.92 ElseIf day = Sunday Then Return 0.18 Else Throw New ArgumentOutOfRangeException("Unexpected enum value") End If End Function
Over and over again the developer is expected to write “ElseIf day =” or “else if (day ==” even though it doesn’t add any additional information. It is just noise that distracts from the important information, namely the day of the week and and the returned value.
In both VB and C#, we can thin this quite a bit by using a case statement.
double CaclRateByDate2(DayOfWeek day) { switch (day) { case DayOfWeek.Monday: return .42; case DayOfWeek.Tuesday: return .67; case DayOfWeek.Wednesday: return .56; case DayOfWeek.Thursday: return .34; case DayOfWeek.Friday: return .78; case DayOfWeek.Saturday: return .92; case DayOfWeek.Sunday: return .18; default: throw new ArgumentOutOfRangeException("Unexpected enum value"); } }
Function CalcRateByDate2(ByVal day As DayOfWeek) As Double Select Case day Case Monday Return 0.42 Case Tuesday Return 0.67 Case Wednesday Return 0.56 Case Thursday Return 0.34 Case Friday Return 0.78 Case Saturday Return 0.92 Case Sunday Return 0.18 Case Else Throw New ArgumentOutOfRangeException("Unexpected enum value") End Select End Function
But even with this, there is still a lot of code that just doesn’t need to be there. Why do you have to keep repeating the fact that you were returning a value? Wouldn’t it be nice if you could write something like this?
double CaclRateByDate2(DayOfWeek day) { return switch (day) { DayOfWeek.Monday: .42; DayOfWeek.Tuesday: .67; DayOfWeek.Wednesday: .56; DayOfWeek.Thursday: .34; DayOfWeek.Friday: .78; DayOfWeek.Saturday: .92; DayOfWeek.Sunday: .18; default: throw new ArgumentOutOfRangeException("Unexpected enum value"); } }
Function CalcRateByDate2(ByVal day As DayOfWeek) As Double Return Select Case day Monday: 0.42 Tuesday: 0.67 Wednesday: 0.56 Thursday: 0.34 Friday: 0.78 Saturday: 0.92 Sunday: 0.18 Case Else Throw New ArgumentOutOfRangeException("Unexpected enum value") End Select End Function
When you eliminate the stuff you don’t actually care about, C# and VB look remarkably similar. All that is left is the pattern you looking for and the result that you want when that pattern is found. This is what is known as Pattern Matching.
Well unfortunately we aren’t going to see something like this in C# 4 or VB 10. But there is a new language on the block that has support for pattern matching. Consider this F# example from Mathew Podwysocki. (Please note that in all these examples a function is being created.)
let calcRateByDay2 (day:System.DayOfWeek) = match day with | 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"
In another example, Mathew shows how to multiple parameters can be checked at the same time. In this example, the underscore is used as a wild card.
let allowUrl url port = match (url, port) with | "http://www.microsoft.com/", 80 -> true | "http://example.com/", 8888 -> true | _, 80 -> true | _ -> false
Unfortunately F# doesn’t have a lock on concise syntax. If you need to do anything interesting with a value then you have to go back to specifying it by name or by a placeholder.
let calcRateByDay3 (day:System.DayOfWeek) = match day with | 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" let calcRateByDay3 (day:System.DayOfWeek) = match day with | _ 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"
By comparison, here is Visual Basic’s syntax for ranges within a case statement.
Function CaclRateByDate3(ByVal day As DayOfWeek) As Double Select Case day Case Monday To Friday : Return 0.42 Case Saturday : Return 0.92 Case Sunday : Return 0.18 Case Else Throw New ArgumentOutOfRangeException("Unexpected enum value") End Select End Function
As you can see, each language in on the .NET platform has certain syntactical strengths that could be applied to the other languages without changing the language’s core nature.