C# 9.0: Target-typed New Expressions – Make Your Initialization Code Less Verbose
In the previous blog posts you learned about different C# 9.0 features:
In this blog post, let’s look at another very interesting feature of C# 9.0 that is called target-typed new
expressions.
Target-typed means that an expression gets the type from the context it is used in. With C# 9.0 the new
expression gets the type from the context, which means you don’t have to specify the target-type explicitly to call a constructor. Let’s look at this with a simple example.
Creating New Objects Before C# 9.0
Let’s define the Friend
class that you see in the code snippet below. As you can see, it has a default constructor and also a constructor with the parameters firstName
and lastName
to initialize the properties FirstName
and LastName
.
public class Friend
{
public Friend() { }
public Friend(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
To create a new instance of this Friend
class, you can use this statement:
Friend friend = new Friend();
You can also use the constructor overload to pass in a firstname and a lastname:
Friend friend = new Friend("Thomas", "Huber");
As the variable gets initialized directly with a new Friend
, you can also use the var
keyword for the variable declaration like below. The C# compiler detects in this case the type Friend
for the variable, as you assign a new Friend
instance to it. The var
keyword was introduced in 2007 with C# 3.0 and .NET Framework 3.5, and it works for local variables.
var friend = new Friend("Thomas", "Huber");
Now, in all those cases above you can see that the new
expression – that’s the part on the right side of the =
sign – always requires the class name. That’s not the case anymore with C# 9.0 if the target-type is already known from the left side of the =
sign, which is always the case if you don’t use the var
keyword on the left side of the =
sign.
Use Your First Target-typed New Expression
With C# 9.0 you can create a new Friend
like below with the target-typed new
expression to call the default constructor. Note that I don’t specify the Friend
class on the right side of the =
sign, the compiler gets it from the left side of the =
sign:
Friend friend = new();
You can also call the overloaded constructor of the Friend
class by passing in a firstname and a lastname:
Friend friend = new("Thomas", "Huber");
But, of course, you can not use the target-typed new
expression when you use the var
keyword to declare your variable, because then the compiler does not have a chance to find out the type that you want to create. So, the following statement does not work:
var friend = new("Thomas", "Huber"); // does NOT work
What About Optional Constructor Parameters?
You could define the Friend
class also with optional constructor parameters like below:
public class Friend
{
public Friend(string firstName = null, string lastName = null)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
Calling that constructor with the target-typed new
expression works exactly the same way as you would call it with a classic new
expression, you just omit the class name on the right side of the =
sign. You could call the constructor for example without any arguments:
Friend friend = new();
You could call it with a firstname:
Friend friend = new("Thomas");
You could call it with firstname and lastname:
Friend friend = new("Thomas", "Huber");
You could also use named arguments to call it with just a lastname:
Friend friend = new(lastName: "Thomas");
So, everything works the same way as before, you just omit the type after the new
keyword.
What’s the Point When You Can Use Var?
You might ask yourself what’s the point of target-typed new
expressions when you can use the var
keyword? What is the advantage of this:
Friend friend = new("Thomas", "Huber");
over this:
var friend = new Friend("Thomas", "Huber");
With the target-typed new
expression you omit the type on the right side of the =
sign, with the var
keyword you omit the type on the left side of the =
sign. For both statements above, the C# compiler produces exactly the same Intermediate Language code. So, it is your decision what you prefer. At least it is your decision for local variables, and so far we just talked about local variables.
Did you notice that the first code snippet in this section with the variable Friend friend
could theoretically be a field of a class? The second code snippet could not be a field of a class, as fields do not support the var
keyword. Fields are one example where target-typed new
expressions are really powerful, because with fields you can not use the var
keyword. And that’s the main point why and where you want to use target-typed new
expressions:
You can use the var
keyword only for local variables. That means target-typed new
expressions are especially powerful to initialize properties and fields, because there the var
keyword is not an option.
Let’s look at these cases where var
is not an option in the next sections, because for all these cases target-typed new
expressions make your code less verbose.
Initialize Properties With Target-typed New Expressions
Look at the class below. It’s a simple, but typical MainViewModel
class that you could use in XAML-based apps, for example WPF or UWP/WinUI, when applying the Model-View-ViewModel pattern (MVVM). It has the properties AddFriendCommand
and Friends
, and both are initialized in the constructor with a classic new
expression.
public class MainViewModel
{
public MainViewModel()
{
AddFriendCommand = new DelegateCommand(AddFriendExecute);
Friends = new ObservableCollection<Friend>();
}
public DelegateCommand AddFriendCommand { get; }
public ObservableCollection<Friend> Friends { get; }
private void AddFriendExecute()
{
Friends.Add(new Friend());
}
}
You can change the code in the constructor to the code that you see below. As you can see, the class names for the initialization are not needed anymore.
Note that also the generic <Friend>
argument for the ObservableCollection<Friend>
is not needed with the target-typed new
expression. So, you don’t write new<Friend>()
, but just new()
.
public MainViewModel()
{
AddFriendCommand = new(AddFriendExecute);
Friends = new();
}
You could also initialize the Friends
property directly like this – and if you ask me, yes, I think this is really beautiful code:
public ObservableCollection<Friend> Friends { get; } = new();
In addition to these adjustments, you can also create the new Friend
object in the AddFriendExecute
method with a target-typed new
expression like this:
private void AddFriendExecute()
{
Friends.Add(new());
}
With the changes applied, the MainViewModel
class looks like this:
public class MainViewModel
{
public MainViewModel()
{
AddFriendCommand = new(AddFriendExecute);
Friends = new();
}
public DelegateCommand AddFriendCommand { get; }
public ObservableCollection<Friend> Friends { get; }
private void AddFriendExecute()
{
Friends.Add(new());
}
}
Beside properties, another very useful case for target-typed new
expressions are fields. Let’s look at fields in the next section.
Initialize Fields With Target-typed New Expressions
Below you see an ErrorViewModelBase
class that contains some typical stuff that I created in several .NET projects. Note that the class is a simplified version that contains just enough to show you the power of target-typed new
expressions. As you can see below, the class contains two new
expressions. One to initialize the field _errorsByPropertyName
, and another one to create a new List<string>
.
public class ErrorViewModelBase
{
protected readonly Dictionary<string, List<string>> _errorsByPropertyName
= new Dictionary<string, List<string>>();
public void AddError(string propertyName, string error)
{
if (!_errorsByPropertyName.ContainsKey(propertyName))
{
_errorsByPropertyName[propertyName] = new List<string>();
}
_errorsByPropertyName[propertyName].Add(error);
}
}
With C# 9.0, you can replace those new
expressions with target-typed new
expressions like shown in the code snippet below. Especially the fact that you don’t have to write new Dictionary<string, List<string>>()
, but just new()
to initialize the field is very powerful. Dictionary<string, List<string>>
is already used and visible in the field declaration, so why duplicate it with the initialization? A target-typed new
expression helps you here to avoid that code duplication.
As mentioned, you can not use the var
keyword for fields, so using var
is not an option here for the _errorsByPropertyName
field.
public class ErrorViewModelBase
{
protected readonly Dictionary<string, List<string>> _errorsByPropertyName = new();
public void AddError(string propertyName, string error)
{
if (!_errorsByPropertyName.ContainsKey(propertyName))
{
_errorsByPropertyName[propertyName] = new();
}
_errorsByPropertyName[propertyName].Add(error);
}
}
Also the creation of a List<string>
in the AddError
method with just new()
instead of new List<string>()
is nice. With these changes, I think this class looks much cleaner and less verbose than before.
When to Use Target-typed New Expressions
There are many cases where I’ll definitely use target-typed new
expressions without a thought. These are the cases where it is obvious to any programmer what new()
will create. For example, look at this property declaration and initialization:
public ObservableCollection<Friend> Friends { get; } = new ObservableCollection<Friend>();
Here you can use a target-typed new
expression without any thought, because it is totally clear from the property declaration that new()
will create an ObservableCollection<Friend>
:
public ObservableCollection<Friend> Friends { get; } = new();
But when you look at the constructor of the MainViewModel
class in the code snippet below, you can not say what the target-typed new
expressions create by just looking at the constructor:
public MainViewModel()
{
AddFriendCommand = new(AddFriendExecute);
Friends = new();
}
When reading the code, you need to look at the types of the properties AddFriendCommand
and Friends
to know what the two new
expressions in the code snippet above are actually creating. But that’s only true if you do not read the code in a tool like Visual Studio, but in a simple text editor or on a blog like this one. If you look at the code in Visual Studio, you can hover any target-typed new
expression with your mouse to see which constructor it calls. In the screenshot below I hovered the second new
expression in Visual Studio and you can see in the tooltip that it actually creates an ObservableCollection<Friend>
.
You can even navigate to the called constructor. In Visual Studio you do this by pressing F12 when the cursor is on the target-typed new
expression. So, with this tooling support, the use of target-typed new
expressions seems to be ok in any case.
Summary
You saw in this blog post different use cases for target-typed new
expressions. I have to admit, I’m already a big fan of target-typed new
expressions. Especially for direct field and property initialization where the var
keyword is not an option, a target-typed new
expression leads to much nicer code.
In the next blog post, you will learn more about the improved pattern matching in C# 9.0.
Happy coding,
Thomas
Comments (24)
Thank you
You’re welcome Avi. And thank you, love it that you took the time to write this comment!
As I was reading through your article I wasn’t really sure of the value that a “target-typed new” gives, but the very last graphic was my “aha!” moment. Without the IDE tool-tip the code describes what is happening in terms of the domain. It is not cluttered with the technical implementation details. If I’m interested in the technical implementation, then I can use the information in the tool-tip. The intent of the code is front and centre, with no distractions (as long as we name things nicely, but that’s a different topic altogether :-) ). Thanks for taking the time to post this. I found it enlightening.
Hey Gordon, thank you! I’m happy to read that you like the article. And I agree to what you wrote about this new syntax.
This seems like exactly the wrong way to go — you want to omit “new”, not the type.
var friend = Friend();
This is what F# and many other languages do. This is especially important for more complex, nested expressions.
With “news” everywhere, it’s a mess.
var ast = Expr(Plus(1,Minus(4,3),Call(‘foo’,’x’,Plus(‘y’,’z’)));
To do this in C#, you’d need to manually create a static helper function to do construction.
The var keyword does not work for fields and properties, it only works for local variables. Which means in other words that all of the options that you’ve mentioned work only for local variables. :-) I hope this makes it a bit clearer that this new syntax is an improvement.
All things before was good, but this is not. Please don’t use it in practice. Painful
Hey Vitali, why do you find this feature not good?
Awesome intro to Target-typed New Expressions.
I’m especially looking forward to this feature, so I only have to write the declaration of complex generic types once :D
Also populating lists will become a bit less verbose when we can omit the type.
Keep up providing awesome content :thumbsup:
Thank you Martin. Yes, absolutely, I’m also looking forward to this feature, I think it can simplify a lot of code. Especially for generic types like you mentioned.
Very much helpfull to improve C# knowledge!
Thank you Wan-Sik, happy to read that it was helpful for you
Good article Thomas. Nicely explained.
I’m not sure I entirely agree that it makes for beautiful code. As you know, I’ve been doing C# a long time and this is all starting to look a bit confusing to me. Doesn’t it to you?
For me, there’s no such thing as beautiful code per se; there are only coders who can or can’t type quickly, and code that’s easy to understand when we come back to maintain it later. Sure, missing out the names of types in initialisations makes it easier to type in the first place, but I doubt I’d be able to understand any of this code later without doing a double-take.
As an example, yesterday I had to read nearly 2000 lines of java to fix a bug. The fix only required a single line, but if I’d have had to go back and forth in the code to work out exactly what objects were being added where (or worse – if I’d had to set up an environment to build the thing just to get compiler hints), it would’ve take me 5 times as long to find where that single line was required, which in itself took 30 seconds to write.
It seems that “features” such as this look great to developers who maybe haven’t had to read much code back yet, but those of us who have, we know the benefit in being able to understand code without requiring tools. And adding more and more syntax to a language for the sole purpose of keeping up with the competition means I’ll have to learn more and more rules to keep being able to understand it.
I’m not against progress, but my question is if this is actually progress or not.
Wouldn’t you agree?
Hey Richard, thank you, happy to read that a pro like you likes the article. :)
Yes, with all the new features the language gets harder to read, as there are many options. I agree to this, the vocabulary gets bigger.
Regarding “beautiful code”, it depends how we interpret it. When I say beautiful code, I don’t think about the numbers of characters that I type. I think more and maybe only about readability and maintainability. Sometimes we can make code super compact by using generic features, but often it gets more complicated. Simplicity is key.
For the target-typed new expressions I think direct field and property initializations make it clear what they do. At other places they’re a bit more magic, and I agree that this can make the code harder to read. It’s a bit similar to the var keyword. When it was introduced, some developers used it only when the class name was obvious from the code on the right side of the equals sign. Today most devs use it everywhere they can. Because I think in the end it is one thing that makes code really readable: Good names.
For example:
person = new();
is better than
p = new();
But we will see how things evolve in the future. We have to work with it. I think it’s progress, but I agree to your comment.
Have a wonderful weekend Richard, made me happy to read here from you!
Thomas
Thank you sir. Great article
Thank you Avi. Happy to read you like it.
Hi
C# appears to be moving closer to VB.Net every day.
Cannot understand why Functions in C# and VB cannot be included in same Class as both language are effectively the same. Heresy to C and C++ devotees. IDE for both languages could easily be tweeked to recognise the difference in code identical except for declarations. Example:
C# public class Friend
{
public Friend() { }
public Friend(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
VB
public class Friend
public sub New
‘Default constructor
end sub ‘substitute for {
public sub New(firstName as string , lastName as string )
FirstName = firstName
LastName = lastName
end sub ‘substitute for }
public Property FirstName as string
public Property LastName as string
end class
What you’re saying is that you want to mix C# and VB.NET in a single source file? That’s not planned, and it would be very hard, as there are different compilers for the different .NET languages like C#, VB.NET and F#.
Thank you, Thomas, for a peek forward at C# 9. The first part about removing “var “ will save keystrokes for the programmer. That will be a help as any programmer can attest. Initializing properties and internal class data using “new()” alone is also a keystroke saver.
But if we are going to make it so easy to initialize properties and class data, will C# 10 automatically initialize our properties and fields for us as we instantiate the class? (That sounds like something an earlier version of basic did.) Of course, if this is the case, then we will have to explicitly set the fields to null if we want them initialized to null.
Hey James, that’s a great question regarding C# 10 and automatically initialized fields and properties. I haven’t heard and read anything about this, and I don’t know if this is going to happen.
I think in the code snippet after
“With the changes applied, the MainViewModel class looks like this:”
you have still
public ObservableCollection Friends { get; }
but it should be:
public ObservableCollection Friends { get; } = new();
and remove the intitialization code
Friends = new();
from the constructor
It’s perfectly valid code but applying these changes will better illustrate this feature.
Hey sharpdoctor, thank you for the feedback. No, I think it’s correct as it is, as I had the original initialization code also in the constructor, so I also wanted to keep it there with target-typed new expressions, so that you can see exactly the same class structure with this new feature. As I wrote, you could initialize the property directly with new(), but that would be the alternative way to this:
public ObservableCollection<Friend> get; } = new ObservableCollection<Friend>();
And I hadn’t this in the original class. I hope this makes it clear what my thoughts were there. :)
Happy coding,
Thomas
Interesting. And if it leads to better naming, everything is fine. Thank you Sir! ;)
You’re welcome Mr Bleile. :)