Getting Setup With C# 9
If you aren’t sure you are using C# 9 and/or you want to start using some of the new shiny features in the C# language, be sure to read our quick guide on getting setup with C# 9 and .NET 5. Any feature written about here is available in the latest C# 9 preview and is not a “theoretical” feature, it’s ready to go!
C# 9 Features
We are slowly working our way through all new C# 9 features, if you are interested in other new additions to the language, check out some of the posts below.
- Improved Target Typing (this post!)
- Relational Pattern Matching
- Init Only Properties
- Top Level Programs
- Record Types
What Is Target Typing?
I’m going to be honest, before C# 9 where they talk about “Improved” Target Typing, I had never actually heard of the term before. But it’s actually very simple. It’s basically a way to say “given the context of what I’m doing, can we infer the type”. The use of the “var” keyword is an example of target typing. The use of var is actually a good example here because it’s actually almost the reverse of the improvements to target typing in C# 9, but let’s jump into those.
Target Typed New Expressions
Target Typed New Expressions is basically just a fancy way of saying that we don’t have to say the type after the new() expression…. That probably doesn’t make it anymore clearer, but in here’s a sample piece of code :
class Person { public string FirstName { get; set; } } class MyClass { void MyMethod() { Person person = new Person(); } }
Now in C# 9, you can do :
class Person { public string FirstName { get; set; } } class MyClass { void MyMethod() { Person person = new(); //<-- This! } }
Since you’ve already defined the type for your variable, it can infer that when you call new() without a type, you’re trying to new up the exact type.
It even works with constructors with parameters!
class Person { public Person(string firstName) { this.FirstName = firstName; } public string FirstName { get; set; } } class MyClass { void MyMethod() { Person person = new("John"); } }
Unfortunately there is a really big caveat. Constructors (I feel anyway) have almost become the minority in my code, especially because of the heavy use of Dependency Injection in todays coding. So typically when I am newing up an object, I am setting properties at the same time like so :
Person person = new Person { FirstName = "John" };
But of course this doesn’t work :
Person person = new { FirstName = "John" };
Because writing new like that without a type, then straight into curly’s tells C# you are creating an “anonymous” object. Interestingly in the current C#9 Preview with Visual Studio, you can do the “double up” approach like so :
Person person = new() { FirstName = "test" };
Intellisense *does not* work right now when doing this (e.g. It won’t auto complete “FirstName” for me), but once it’s written, it will compile. I chuckle because at the moment if you tried this in C#8, it would gray out the () because they aren’t needed. And typically in a code review someone will add a nit that “Hey, you don’t need the parenthesis”, but now I guess there is a reason to have them!
Another huge caveat is the use of the “var” keyword. For obvious reasons, this doesn’t work :
var person = new();
As I mentioned earlier, this is almost like using var, but coming from the other side. If your code doesn’t use the var keyword that often, then this may be of use, but for me, I almost exclusively use var these days when newing up objects, so it’s not going to be a common tool in my arsenal.
That being said, cases where you cannot use var (Class level properties, returning new objects from a method etc), this fits perfectly.
Target Typing Conditional Operators
What we are about to talk about *should* work in C# 9, but under the current preview doesn’t. Apparently in a previous preview version it did but.. Right now it’s a bit busted. Frustrating! But we’ll still talk about it anyway because it’s under the umbrella of “Target Typing”.
Target typed conditional operators are basically the compiler looking for ways to make your null-coalescing operator (??) work. For example the following code :
class Program { static void Main(string[] args) { Cat cat = null; Dog dog = new Dog(); IAnimal animal = cat ?? dog; } } interface IAnimal { } class Dog : IAnimal { } class Cat : IAnimal { }
Notice how Cat and Dog both inherit from the same interface (IAnimal), and we are checking if Cat is null, then return Dog into a variable with a type of “IAnimal”. Right now in C# 8 (And.. C# 9 Preview 7), this doesn’t work and the compiler complains. But it makes total sense for this code to work because the developers intent is clear, and it doesn’t break any existing paradigms with the language.
For example doing this does work in all versions of C#:
Cat cat = null; Dog dog = new Dog(); IAnimal animal = cat; if (animal == null) animal = dog;
So it’s really just allowing the ?? operator to do this for us.
Another example not using classes would be something such as :
Dog dog = null; int? myVariable = dog == null ? 0 : null;
This fails because there is no conversion between 0 and null. But we are casting it back to a nullable integer so clearly there is a common base here and the developers intent is pretty clear. In fact this does work if you do something such as :
Dog dog = null; int? myVariable = dog == null ? 0 : default(int?);
Or even
Dog dog = null; int? myVariable = dog == null ? (int?)0 : null;
So again, this is all about wrapping some sugar around this to do things we already do and make developers lives easier.
Again I must stress that the improved target typing for conditional operators are not quite in the preview yet, but should be there very soon.
Troubleshooting
Just a quick note for this feature. If you see :
The feature 'target-typed object creation' is currently in Preview and *unsupported*.
Then you *must* have the latest preview SDK installed and your csproj file updated to handle C# 9. More info here : https://dotnetcoretutorials.com/2020/08/07/getting-setup-with-c-9-preview/
If you’ve abandoned meaningful constructors which fully initialize your objects, solely because of the dependency injection library you’re using, then you’re either using a bad DI library or you’re using your DI library poorly.
All the modern DI libraries I’m aware of allow you to leverage parameterized constructors.
I think it’s more that, for “services”, “repositories” and the like, I’m not new-ing up objects at all – since I’m using Dependency Injection. For POCOs, Models, etc that I *am* newing up, I’m generally using object initializers not constructors. Does that make more sense?
hi
thank you for share this post .
I have a question.
you know what does this do?
example :
long a
// in use
x = @a;
thank you