How To Handle Cases By Range in C# (and JavaScript)
Sometimes you need to handle cases within a certain range, e.g., you have a customer and you need to apply different treatments depending on the salary value.
Although it's looks like a classic switch statement, Until C# 7.0 the case label must have been a static value, and you couldn’t use expression as a case ??
Before C# 7.0
With no support of the language, the primitive way to do it was by using if/else statements:
static string HandleCustomers(Customer customer) { if (customer.salary >= 40_000) { return HandleRichCustomer(customer); } if (customer.salary >= 20_000 && customer.salary < 40_000) { return HandleWealthyCustomer(customer); } if (customer.salary >= 10_000 && customer.salary < 20_000) { return HandleAverageCustomer(customer); } if (customer.salary >= 00_000 && customer.salary < 10_000) { return HandlePoorCustomer(customer); } throw new IllegalSalaryException(); }
As you can see, the code is very long and get even longer with more logic...
C# 7.0
C# 7.0 added two new features to the switch statement: 'Type pattern' and 'When clause'.
Type pattern:
The Type pattern added the ability to perform pattern matching by type, and apply a conversion type to the object:
static string TypePattern<T>(T customer) { switch (customer) { case RichCustomer richCustomer: return $"RichCustomer salary: {richCustomer.salary}"; case WealthyCustomer wealthyCustomer: return $"WealthyCustomer salary: {wealthyCustomer.salary}"; case AverageCustomer averageCustomer: return $"AverageCustomer salary: {averageCustomer.salary}"; case PoorCustomer poorCustomer: return $"RichCustomer salary: {poorCustomer.salary}"; default: throw new IllegalSalaryException() } }
When clause:
The when clause is an additional condition which you can add to the case, using the when label:
static string WhenClause<T>(T Customer) { switch (Customer) { case Customer richCustomer when richCustomer.salary >= 40_000: return HandleRichCustomer(richCustomer); case Customer wealthyCustomer when wealthyCustomer.salary < 40_000 && wealthyCustomer.salary >= 20_000: return HandleWealthyCustomer(wealthyCustomer); case Customer avaregeCustomer when avaregeCustomer.salary < 20_000 && avaregeCustomer.salary >= 10_000: return HandleAverageCustomer(avaregeCustomer); case Customer poorCustomer when poorCustomer.salary < 10_000 && poorCustomer.salary >= 0: return HandlePoorCustomer(poorCustomer); default: throw new IllegalSalaryException(); } }
C# 8.0
C# 8.0 came and introduced a new feature: 'switch expression'. Instead of cases you now have multiple expressions, which are separated by a comma, The first match of the pattern on the right, returns the expression on the left.
Customer c => "I am a customer"
(You can read here about more new patterns, as: "positional pattern", "Property Pattern" and "Tuple pattern")
With the 'switch expression' you can write the same code, shorter:
static string HandleCustomers(Customer customer) => customer.salary switch { int salary when salary >= 40_000 => HandleRichCustomer(customer), int salary when salary >= 20_000 && salary < 40_000 => HandleWealthyCustome r(customer), int salary when salary >= 10_000 && salary < 20_000 => HandleAverageCustomer(customer), int salary when salary >= 00_000 && salary < 10_000 => HandlePoorCustomer(customer), _ => throw new IllegalSalaryException() });
(Notice the Discard Pattern ('_') that match any expression)
C# 9.0
The latest version of C#, came with some more new features:
New Logical Patterns:
C# 9.0 added (finally! ??) new logical operators: 'and', 'or' and 'not'.
Relational Pattern:
The Relational Pattern lets you compare constant expression (int, enum, char or float) with relational operator ('<’, ‘>', '<=', '>=').
So, in C# 9.0, our 'HandleCustomer' function can be written like this:
static string HandleCustomers(Customer customer) => customer.salary switch { >= 40_000 => HandleRichCustomer(customer), >= 20_000 and < 40_000 => HandleWealthyCustomer(customer), >= 10_000 and < 20_000 => HandleAverageCustomer(customer), >= 00_000 and < 10_000 => HandlePoorCustomer(customer), _ => throw new IllegalSalaryException() };
Short and clean ??
(You can read here all the patterns in the C# language).
JavaScript
As of today, despite the significant growth of the language, JavaScript doesn't yet support all of the new C# features.
But you can still achieve the same result by using switch(true).
Because JavaScript allows the case label to be an expression, you can do a switch statement on the 'true' key word and it will enter the first case expression that returns true:
(I added a picture because for some reason, the linkedin atricle got 'broken' with the upper code ???♂?)
Thanks,
Ziv Ben-Or
+972545882011