If
Pattern matching with the if statement
A feature introduced with C# 7 and later is pattern matching. The if statement can use the is keyword in combination with declaring a local variable to make your code safer:
- Add statements so that if the value stored in the variable named o is an int, then the value is assigned to the local variable named i, which can then be used inside the if statement. This is safer than using the variable named o because we know for sure that i is an int variable and not something else, as shown in the following code:
// Add and remove the "" to change between string and int.
object o = “3”; int j = 4;
if (o is int i) {
WriteLine($“{i} x {j} = {i * j}”); }
else
{
WriteLine(“o is not an int so it cannot multiply!”);
}
-
Run the code and view the results, as shown in the following output:
o is not an int so it cannot multiply! -
Delete the double-quote characters around the “3” value so that the value stored in the variable named o is an int type instead of a string type.
-
Rerun the code to view the results, as shown in the following output:
3 x 4 = 12
Switch
Like the if statement, the switch statement supports pattern matching in C# 7 and later. The case values no longer need to be literal values; they can be patterns.
In C# 7 and later, your code can more concisely branch, based on the subtype of a class, and you can declare and assign a local variable to safely use it. Additionally, case statements can include a when keyword to perform more specific pattern matching.
class Animal // This is the base type for all animals. {
public string? Name;
public DateTime Born;
public byte Legs;
}
class Cat : Animal // This is a subtype of animal. {
public bool IsDomestic;
}
class Spider : Animal // This is another subtype of animal. {
public bool IsPoisonous;
}
var animals = new Animal?[]
{
new Cat { Name = "Karen", Born = new(year: 2022, month: 8, day: 23), Legs = 4, IsDomestic = true },
null,
new Cat { Name = "Mufasa", Born = new(year: 1994, month: 6, day: 12) },
new Spider { Name = "Sid Vicious", Born = DateTime.Today, IsPoisonous = true},
new Spider { Name = "Captain Furry", Born = DateTime.Today }
} ;
foreach (Animal? animal in animals)
{
string message;
switch (animal)
{
case Cat fourLeggedCat when fourLeggedCat.Legs == 4:
message = $"The cat named {fourLeggedCat.Name} has four legs."; break;
case Cat wildCat when wildCat.IsDomestic == false:
message = $"The non-domestic cat is named {wildCat.Name}."; break;
case Cat cat:
message = $"The cat is named {cat.Name}."; break;
default: // default is always evaluated last.
message = $"{animal.Name} is a {animal.GetType().Name}."; break;
case Spider spider when spider.IsPoisonous:
message = $"The {spider.Name} spider is poisonous. Run!"; break;
case null:
message = "The animal is null."; break;
}
Console.WriteLine($"switch statement: {message}");
} Switch Expression
Most switch statements are very simple, yet they require a lot of typing. switch expressions are de- signed to simplify the code you need to type while still expressing the same intent in scenarios where all cases return a value to set a single variable. switch expressions use a lambda, =>, to indicate a return value.
Console.WriteLine(" Using Switch Expression ");
foreach (Animal? animal in animals)
{
string message;
message = animal switch
{
Cat fourLeggedCat when fourLeggedCat.Legs == 4 => $"The cat named {fourLeggedCat.Name} has four legs.",
Cat wildCat when wildCat.IsDomestic == false => $"The non-domestic cat is named {wildCat.Name}.",
Cat cat => $"The cat is named {cat.Name}.",
Spider spider when spider.IsPoisonous => $"The {spider.Name} spider is poisonous. Run!",
null => "The animal is null.",
_ => $"{animal.Name} is a {animal.GetType().Name}." // break or discar pattern
};
Console.WriteLine($"switch expression: {message}");
}This is a great example of modern C# pattern matching with switch expressions! It showcases how powerful and expressive C#‘s switch syntax has become.
However, there is one syntax error in your code that will prevent it from compiling:
The Bug: The Discard Pattern
At the very bottom, you have:
- => $"{animal.Name} is a {animal.GetType().Name}."
In C#, the catch-all “default” case (called the discard pattern) must use an underscore (_), not a hyphen/minus sign (-). It should be:
_ => $"{animal.Name} is a {animal.GetType().Name}."
Key Concepts Demonstrated in Your Code
- Top-to-Bottom Evaluation Order
- C# evaluates switch arms sequentially from top to bottom and executes the first matching branch.
- For example, if a
Cathas 4 legs, it matches the first condition (Cat fourLeggedCat when fourLeggedCat.Legs == 4). Even though it is also aCat, it won’t fall through to the generalCat catbranch because the first match stops the evaluation.
- Type Patterns & Declarative Variables
- Instead of writing explicit
ischecks and casting (if (animal is Cat c)), the switch expression matches the type and instantly assigns it to a locally scoped variable (fourLeggedCat,wildCat,spider) in one clean step.
- When Guards (Condition Filters)
- The
whenkeyword adds extra Boolean logic to a pattern without needing nestedifstatements inside your blocks (e.g.,when fourLeggedCat.Legs == 4orwhen spider.IsPoisonous).
- Explicit Null Handling
- Because
animalscontainsAnimal?(nullable items), thenull => "The animal is null."arm safely interceptsnullreferences before they can throw aNullReferenceExceptionwhen trying to access properties likeanimal.Name.