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:

  1. 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!”);

}

  1. Run the code and view the results, as shown in the following output:

       o is not an int so it cannot multiply!
    
  2. 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.

  3. 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

  1. Top-to-Bottom Evaluation Order
  • C# evaluates switch arms sequentially from top to bottom and executes the first matching branch.
  • For example, if a Cat has 4 legs, it matches the first condition (Cat fourLeggedCat when fourLeggedCat.Legs == 4). Even though it is also a Cat, it won’t fall through to the general Cat cat branch because the first match stops the evaluation.
  1. Type Patterns & Declarative Variables
  • Instead of writing explicit is checks 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.
  1. When Guards (Condition Filters)
  • The when keyword adds extra Boolean logic to a pattern without needing nested if statements inside your blocks (e.g., when fourLeggedCat.Legs == 4 or when spider.IsPoisonous).
  1. Explicit Null Handling
  • Because animals contains Animal? (nullable items), the null => "The animal is null." arm safely intercepts null references before they can throw a NullReferenceException when trying to access properties like animal.Name.