public decimal CalculateDiscount(Order order) =>
order switch
( > 10, > 1000.00m) => 0.10m,
( > 5, > 50.00m) => 0.05m,
{ Cost: > 250.00m } => 0.02m,
null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
var someObject => 0m,
The preceding code demonstrates the
positional pattern
where the properties are deconstructed for the expression.
List patterns
You can check elements in a list or an array using a
list pattern
. A
list pattern
provides a means to apply a pattern to any element of a sequence. In addition, you can apply the
discard pattern
(
_
) to match any element, or apply a
slice pattern
to match zero or more elements.
List patterns are a valuable tool when data doesn't follow a regular structure. You can use pattern matching to test the shape and values of the data instead of transforming it into a set of objects.
Consider the following excerpt from a text file containing bank transactions:
04-01-2020, DEPOSIT, Initial deposit, 2250.00
04-15-2020, DEPOSIT, Refund, 125.65
04-18-2020, DEPOSIT, Paycheck, 825.65
04-22-2020, WITHDRAWAL, Debit, Groceries, 255.73
05-01-2020, WITHDRAWAL, #1102, Rent, apt, 2100.00
05-02-2020, INTEREST, 0.65
05-07-2020, WITHDRAWAL, Debit, Movies, 12.57
04-15-2020, FEE, 5.55
It's a CSV format, but some of the rows have more columns than others. Even worse for processing, one column in the WITHDRAWAL
type has user-generated text and can contain a comma in the text. A list pattern that includes the discard pattern, constant pattern and var pattern to capture the value processes data in this format:
decimal balance = 0m;
foreach (string[] transaction in ReadRecords())
balance += transaction switch
[_, "DEPOSIT", _, var amount] => decimal.Parse(amount),
[_, "WITHDRAWAL", .., var amount] => -decimal.Parse(amount),
[_, "INTEREST", var amount] => decimal.Parse(amount),
[_, "FEE", var fee] => -decimal.Parse(fee),
_ => throw new InvalidOperationException($"Record {string.Join(", ", transaction)} is not in the expected format!"),
Console.WriteLine($"Record: {string.Join(", ", transaction)}, New balance: {balance:C}");
The preceding example takes a string array, where each element is one field in the row. The switch
expression keys on the second field, which determines the kind of transaction, and the number of remaining columns. Each row ensures the data is in the correct format. The discard pattern (_
) skips the first field, with the date of the transaction. The second field matches the type of transaction. Remaining element matches skip to the field with the amount. The final match uses the var pattern to capture the string representation of the amount. The expression calculates the amount to add or subtract from the balance.
List patterns enable you to match on the shape of a sequence of data elements. You use the discard and slice patterns to match the location of elements. You use other patterns to match characteristics about individual elements.
This article provided a tour of the kinds of code you can write with pattern matching in C#. The following articles show more examples of using patterns in scenarios, and the full vocabulary of patterns available to use.
See also
Use pattern matching to avoid 'is' check followed by a cast (style rules IDE0020 and IDE0038)
Exploration: Use pattern matching to build your class behavior for better code
Tutorial: Use pattern matching to build type-driven and data-driven algorithms
Reference: Pattern matching