C# tutorials > Modern C# Features > C# 6.0 and Later > What are recursive patterns in C# 8.0?
What are recursive patterns in C# 8.0?
Recursive patterns in C# 8.0 are a powerful feature that extends pattern matching capabilities by allowing you to match properties of nested objects and deconstruct objects in a recursive manner. They are particularly useful for working with complex data structures where you need to examine multiple levels of an object hierarchy.
Basic Recursive Pattern Example
This example demonstrates a simple recursive pattern. We have a Person
class with a nested Address
class. The GetCity
method uses pattern matching to extract the city from the HomeAddress
. The first case { HomeAddress: { City: "London" } }
checks if the city is London. The second case { HomeAddress: { City: var city } }
extracts the city into a variable named city
.
public class Address
{
public string Street { get; set; }
public string City { get; set; }
}
public class Person
{
public string FirstName { get; set; }
public Address HomeAddress { get; set; }
}
public static class PatternMatchingExample
{
public static string GetCity(Person person)
{
return person switch
{
{ HomeAddress: { City: "London" } } => "Person lives in London",
{ HomeAddress: { City: var city } } => $"Person lives in {city}",
_ => "Unknown location"
};
}
}
Concepts Behind the Snippet
The key concepts at play here are:
person.HomeAddress.City
) directly within the pattern.{ PropertyName: Pattern }
is the syntax for matching a property against a pattern.var
keyword can be used to capture the value of a property._
) to ignore a property if you don't need its value.
Real-Life Use Case: Configuration Validation
Consider a configuration object with nested security settings. Recursive patterns can be used to validate that all required fields are present. This example validates that the ConnectionString
, Username
, and Password
properties are not null. The not null
pattern checks if a value is not null.
public class DatabaseConfig
{
public string ConnectionString { get; set; }
public SecurityConfig Security { get; set; }
}
public class SecurityConfig
{
public string Username { get; set; }
public string Password { get; set; }
}
public static class ConfigValidator
{
public static bool IsValid(DatabaseConfig config)
{
return config switch
{
{ ConnectionString: not null, Security: { Username: not null, Password: not null } } => true,
_ => false
};
}
}
Deconstruction with Recursive Patterns
Recursive patterns can also be combined with deconstruction. The Point
class defines a Deconstruct
method. The ProcessPoint
method then uses recursive patterns with deconstruction to match different point coordinates. The pattern (0, 0)
matches the origin. The pattern (var x, 0)
matches any point on the X-axis.
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public void Deconstruct(out int x, out int y)
{
x = X;
y = Y;
}
}
public static class PointProcessor
{
public static string ProcessPoint(Point point)
{
return point switch
{
(0, 0) => "Origin",
(var x, 0) => $"X-axis: {x}",
(0, var y) => $"Y-axis: {y}",
(var x, var y) => $"({x}, {y})"
};
}
}
Best Practices
Here are some best practices for using recursive patterns:
Interview Tip
When discussing recursive patterns in an interview, be prepared to explain how they improve code readability and maintainability, especially when dealing with complex object hierarchies. Also, be prepared to discuss null handling strategies when using recursive patterns.
When to Use Them
Use recursive patterns when:
Memory Footprint
Recursive patterns themselves don't significantly impact memory footprint. However, if you are creating new objects within the pattern (e.g., capturing values into new variables), that will contribute to memory allocation. Be mindful of creating unnecessary objects within your patterns.
Alternatives
Alternatives to recursive patterns include:
if
statements: These can be used, but they often lead to more verbose and less readable code.?.
) and null-coalescing operators (??
): These can help with null handling but don't provide the same level of pattern matching.
Pros
if
statements with a single pattern.
Cons
FAQ
-
What happens if a property in a recursive pattern is null?
If a property in a recursive pattern is null and you don't handle it, you will likely get a
NullReferenceException
. Use nullable types or explicitly check for null values within your pattern or before using the pattern matching. -
Can I use recursive patterns with custom classes and structs?
Yes, you can use recursive patterns with any class or struct, as long as the properties you are matching are accessible.
-
Are there performance considerations when using recursive patterns?
In most cases, the performance difference between recursive patterns and traditional
if
statements is negligible. However, very complex patterns might have a slight performance impact. Profile your code if performance is critical.