C# tutorials > Modern C# Features > C# 6.0 and Later > Explain pattern matching in C# with examples of `is` and `switch` expressions.
Explain pattern matching in C# with examples of `is` and `switch` expressions.
Understanding Pattern Matching in C#
Pattern matching is a feature introduced in C# 7.0 and enhanced in later versions, allowing you to conditionally extract values from objects based on their structure and data. It provides a more concise and readable way to write code that handles different data types and object states. Two key constructs for pattern matching are the is
and switch
expressions.
Pattern Matching with the `is` Expression
The In this example, if is
expression checks if an object is of a specific type and, if so, assigns it to a new variable of that type. This allows you to directly access the properties of the object without needing a separate cast. The code becomes much cleaner and easier to read.shape
is a Circle
, it's automatically cast to a Circle
object named c
, and you can access c.Radius
. The same logic applies to the Rectangle
case.
public static string GetShapeDescription(object shape)
{
if (shape is Circle c)
{
return $"This is a circle with radius {c.Radius}";
}
else if (shape is Rectangle r)
{
return $"This is a rectangle with width {r.Width} and height {r.Height}";
}
else
{
return "Unknown shape.";
}
}
Pattern Matching with the `switch` Expression
The The switch
expression provides a more powerful way to perform pattern matching, allowing you to handle multiple cases in a concise manner. Each case
can include a type pattern and a variable declaration. It can even match against null values.switch
expression evaluates the shape
object and executes the code block corresponding to the matching pattern. Similar to the is
example, it casts the object to the specified type and assigns it to a variable for easy access to its properties.
public static string GetShapeDescriptionSwitch(object shape)
{
switch (shape)
{
case Circle c:
return $"This is a circle with radius {c.Radius}";
case Rectangle r:
return $"This is a rectangle with width {r.Width} and height {r.Height}";
case null:
return "Shape is null.";
default:
return "Unknown shape.";
}
}
Concepts Behind the Snippet
null
.
Real-Life Use Case
Consider a scenario where you're processing different types of commands. Using pattern matching, you can easily determine the type of command and execute the appropriate action. In this example, the ProcessCommand
method takes an ICommand
object and uses a switch
expression to determine if it's a CreateUserCommand
or an UpdateUserCommand
. It then executes the command accordingly. This removes the need for multiple if
statements or type checks, making the code more maintainable.
public interface ICommand { void Execute(); }
public class CreateUserCommand : ICommand { public string Username { get; set; } public void Execute() { Console.WriteLine($"Creating user {Username}"); } }
public class UpdateUserCommand : ICommand { public int UserId { get; set; } public string NewUsername { get; set; } public void Execute() { Console.WriteLine($"Updating user {UserId} to {NewUsername}"); } }
public static void ProcessCommand(ICommand command)
{
switch (command)
{
case CreateUserCommand createUser:
createUser.Execute();
break;
case UpdateUserCommand updateUser:
updateUser.Execute();
break;
default:
Console.WriteLine("Unknown command.");
break;
}
}
Best Practices
default
case in your switch
expressions to handle unexpected or unknown types._
): If you only care about the type and not the variable, use a discard (_
) to avoid compiler warnings about unused variables (e.g., case Circle _:
).
Interview Tip
Be prepared to explain the benefits of pattern matching over traditional type checking and casting. Highlight the improved readability, conciseness, and safety it provides. Also, be ready to discuss the different types of patterns available and when to use each one.
When to Use Them
Memory Footprint
Pattern matching itself doesn't add significant overhead. The memory footprint is primarily determined by the objects being matched and the variables being created. If the matched object already exists, there's no additional memory allocation for casting with is
or switch
as the cast is essentially a compile-time check. Just remember to avoid allocating large temporary variables inside of your pattern matching blocks.
Alternatives
if (obj is Type)
followed by a cast to access the object's properties. This approach is less concise and can lead to redundant code.
Pros
Cons
FAQ
-
What is the difference between `is` and `as`?
The
is
operator checks if an object is of a specific type and returns a boolean value (true
orfalse
). Theas
operator attempts to cast an object to a specific type and returns the object if the cast is successful; otherwise, it returnsnull
. Theis
operator in conjunction with pattern matching can declare a new variable of the target type directly if the type check passes. This avoids the need for a separate cast. -
Can I use pattern matching with properties?
Yes, you can use property patterns (introduced in C# 8.0 and later) to match objects based on the values of their properties. For example:
case Person { Age: > 18, Name: "John" }:
. This allows you to check multiple conditions on an object's properties in a single pattern. -
Does pattern matching introduce performance overhead?
In most cases, the performance overhead of pattern matching is minimal. The compiler optimizes pattern matching expressions to be as efficient as possible. However, very complex patterns with many conditions might have a slight performance impact, but it's unlikely to be significant in most real-world scenarios. Always profile your code if performance is a concern.