C# 10.0: Extended Property Patterns – Use the Dot Token to Access Nested Members
In the previous blog posts you learned about different C# 10.0 features:
In this blog post, let’s look at another interesting feature of C# 10.0 which is called extended property patterns.
Property Patterns in C# 9.0
I’ve written a bigger blog post about pattern matching in C# 9.0. If you’re not familiar with pattern matching, I recommend you to read that blog post.
In that blog post, we created a class hierarchy like you see it below. As you can see, there is a Person
class, and the two classes Developer
and Manager
that inherit from Person
. Note also that the Developer
class has a Manager
property of type Manager
.
public class Person
{
public string? FirstName { get; set; }
public int YearOfBirth { get; set; }
}
public class Developer : Person
{
public Manager? Manager { get; set; }
}
public class Manager : Person
{
}
If you want to check if a variable contains a Developer
instance that has a Manager
with the firstname Thomas, you can do that in C# 9.0 as below:
if (obj is Developer { Manager: { FirstName: "Thomas" } } developerWithThomasAsManager)
{
// Use the developerWithThomasAsManager variable here
}
Extended Property Patterns in C# 10.0
In C# 10.0, you can use the .
token to access members in a property pattern. This is called extended property patterns. The code snippet below uses the .
token to check if the Manager
‘s FirstName
property contains the value Thomas. This means that the code snippet below does exactly the same as the code snippet above.
if (obj is Developer { Manager.FirstName: "Thomas" } developerWithThomasAsManager)
{
// Use the developerWithThomasAsManager variable here
}
Important to mention is that if the Manager
property of the Developer
instance is null
, you do not get a NullReferenceException
. You could think that you get one, because you wrote Manager.FirstName
. But remember, you’re writing a pattern here to check the shape of the object, you’re not directly accessing that property with your code, so there won’t be a NullReferenceException
. That means if the Manager
property is null
, the pattern { Manager.FirstName: "Thomas" }
does not match, and the condition of the if
statement above evaluates to false
, and the if
block is not entered. That’s it.
The More Nesting You Have, the Greater This Feature Is
Let’s say you want to check if the Manager
‘s firstname has a length of 6. In C# 9.0 you do this by nesting property patterns to access the Length
property of the string
type, which is the type of the FirstName
property. You see this in the code snippet below:
if (obj is Developer { Manager: { FirstName: { Length: 6 } } } developerWithManagerFirstnameLengthOfSix)
{
// Use the developerWithManagerFirstnameLengthOfSix variable here
}
In the next code snippet you see the C# 10.0 way to check if a developer’s manager has a firstname with a length of 6. The extended property patterns with the .
token make this super easy to read:
if (obj is Developer { Manager.FirstName.Length: 6 } developerWithManagerFirstnameLengthOfSix)
{
// Use the developerWithManagerFirstnameLengthOfSix variable here
}
Use Multiple Extended Property Patterns
You can also use multiple extended property patterns. What if you want to check for example if a developer has a manager who has a firstname with a length of 6 and who (=the manager) is born in 1980? Below you see the C# 9.0 way to do this:
if (obj is Developer { Manager: { FirstName: { Length: 6 }, YearOfBirth: 1980 } } dev)
{
// Use the dev variable here
}
In C# 10.0, you can write the same code as below with an extended property pattern to access the Length
property of the manager’s firstname:
if (obj is Developer { Manager: { FirstName.Length: 6, YearOfBirth: 1980 } } dev)
{
// Use the dev variable here
}
You can even go further in C# 10.0 and use the .
token also to access the Manager
‘s FirstName
and YearOfBirth
properties like you see it in the code snippet below:
if (obj is Developer { Manager.FirstName.Length: 6, Manager.YearOfBirth: 1980 } dev)
{
// Use the dev variable here
}
Personally, I am a fan of the very last code snippet here, because when I read it, I recognize very fast that the pattern checks the properties FirstName
and YearOfBirth
of the Manager
, and not those of the Developer
. So, using the .
token can increase readability.
Summary
As you saw in this blog post, extended property patterns are a little C# 10.0 language extension that let’s you use the .
token to access nested properties when you use a property pattern. As mentioned at the beginning of this blog post, if you’re not familiar with pattern matching in C#, my blog post about pattern matching in C# 9.0 is a good read.
Comments (7)
I wonder how come anyone finds either option (the existing and the updated) anywhere elegant, usable, or permissible. In my opinion this is horrible, I’ll never let it into a codebase I curate.
Especially the part where operator == is replaced by : (and nowhere else in the language).
I think you’re looking at it wrong. Don’t think of it as an expression. Think of it as a chunk of JSON. This code:
{ Manager: { FirstName.Length: 6, YearOfBirth: 1980 } }
Is kind of like an anonymous object, described in JSON. This object’s Manager property has a FirstName whose length is 6, and a YearOfBirth property with the value 1980. You’re saying you want the developers that look more or less like this. The fact that they’ve sort of cheated the syntax into something that’s pseudo-json-ish to save space is a good thing. No-one ever said you HAD to use the pattern matching syntax, just that you can accomplish the same thin in less space if you do.
Thanks for the fantastic answer Mel. I agree 100%, thanks for the great JSON analogy. It’s exactly this!
“Think of it as a chunk of JSON. … kind of like an anonymous object”
If a strongly typed language uses a “chunk of JSON” to save a few explicit commands, at the cost of semantic confusion, and inconsistency, is it still a strongly typed language ? If so, why not shoehorn Lisp, or APL,, chunks into C# ?
” you want the developers” In this case you “want” to test a single instance of ‘developer for matching field values. Under the hood, you are using a method that tests two fields, and uses logical ‘And.
pity,
Developer developer = new () { Manager.FirstName.Length: 6, Manager.YearOfBirth: 1980 }
NOT work
Yeah, as that would be an object initialization and not a pattern to test. :)
It cannot work, as for example Length is a readonly property of the string type, and so setting Manager.FirstName.Length:6 makes no sense. Manager.FirstName=”something” could make sense, but who creates the Manager object then? I think there are many questions to answer before we get a syntax like this. With structs instead of classes we could come close to it. But maybe instead of doing that, this is what constructors are made for.
if (obj is Developer { Manager.FirstName.Length: 6, Manager.YearOfBirth: 1980 } dev)
{
// Use the dev variable here
}
imho, the “hidden” assignment to ‘dev … which does not require explicit type declaration … is obscure, and violates the strongly typed nature of C#. Yes, structurally, there is nesting in this example, but that obfuscates what is happening which is a logical ‘And of two conditions.
This kind of “black box” embodying “spooky action at a distance” is a potential source of confusion.