In the previous post, we saw how C# 9.0 introduced the init only
properties. In this blog post, we will explore some more of the language features which would be introduced in C# 9.0.
Top Level Programs
One of the annonying quality of any programming language is the baggage of boiler plate code that needs to be written for trying out a one-line. For example, prior to C# 9, even hello word looks like following.
using System;
namespace CSharp9.TopLevelPrograms
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
That is plenty of boiler plate code for a simple hello world program. There was hardly any improvements in this regard, with the exception of static directive
which was introduced in C# 6.
using static System.Console;
namespace CSharp9.TopLevelPrograms
{
class Program
{
static void Main(string[] args)
{
WriteLine("Hello World!");
}
}
}
Even with that, there were plenty of boiler plate code. All that comes to an end with the introduction of Top Level Program. Yo could no do away with explicit Main
and other boiler plate codes. Starting with C# 9, you could rewrite the above code as
using System;
Console.WriteLine("Hello World");
Or including the static directive
using static System.Console;
WriteLine("Hello World");
If you are trying out a new language feature, this would be highly useful. No longer do you need to all those unncessary boiler plate codes. Behind the scenes, nothing has changed, it is the same old Main
which the compiler has generated.
// $Program
using System;
private static void $Main(string[] args)
{
Console.WriteLine("Hello World");
}
The Top Level Programs are not limited to above, you could also write methods and classes. The methods gets converted to local methods
. For example,
using static System.Console;
WriteLine($"Hello {GetName()}");
WriteLine($"Hello {new Foo().GetName()}");
string GetName() => "Anu Viswan";
class Foo
{
public string GetName() => "Anu Viswan";
}
The above code gets converted to
using System;
using System.Runtime.CompilerServices;
[CompilerGenerated]
internal static class $Program
{
private static void $Main(string[] args)
{
Console.WriteLine("Hello " + GetName());
Console.WriteLine("Hello " + new Foo().GetName());
static string GetName()
{
return "Anu Viswan";
}
}
}
internal class Foo
{
public string GetName()
{
return "Anu Viswan";
}
}
As you can see, behind the scenes, the compiler does all the magic to create the regular code, but enables the developers to write sample codes within minimum code.
Targeted Type ‘new’ Expression
How often have you wondered _what if, what if the compiler could detect the targeted type, without the developer having the mention it explicitly`. For example, consider the following code
public class Foo
{
public string FirstName { get; set; }
public string Get(Bar bar) => bar.UserName;
}
public class Bar
{
public string UserName { get; set; }
}
// client code
Dictionary<string, string> dictionary = new Dictionary<string,string>
{
["Name1"] = "Anu Viswan",
["Name2"] = "Jia Anu"
};
Foo foo = new Foo{ FirstName = nameof(Foo.FirstName) };
foo.Get(new Bar{ UserName = nameof(Bar.UserName) });
If you observe the client code, you could be left wondering
- Why do you need to the Dictionary Type explicitly defined on Right Hand side when the assigned type has been defined.
- Same is the case with the type
Foo
on second line of client code. - Why not omit the type when you know the method expects the particular type in the last line ? The compiler already knows that the method
Get()
accepts a parameter type ofBar
.
All these are now answered by in C# 9.0 with the Targeted Type new
expression. For example, now you could skip the duplicate types and omit the types when the types could be infered.
The above code could be rewritten as
Dictionary<string, string> dictionary = new()
{
["Name1"] = "Anu Viswan",
["Name2"] = "Jia Anu"
};
Foo foo = new (){ FirstName = nameof(Foo.FirstName) };
foo.Get(new() { UserName = nameof(Bar.UserName) });
Now isn’t that better. I am more excited about the last line, where you could skip the Type name if it could be inferred.
We will continue our exploration of the language features in upcoming posts.