C# is awesome programming language. The C# team at Microsoft is adding new features with every new version of the language. And forthcoming version C# 9 is no exception. Among other nice features, there will also be a feature called Top-level statements.
You can read more about proposal on this link https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/top-level-statements. Furthermore, you can find language feature status here https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md#c-9.
The basic motivation for this feature:
There’s a certain amount of boilerplate surrounding even the simplest of programs, because of the need for an explicitC# 9 proposal document, here
Mainmethod. This seems to get in the way of language learning and program clarity. The primary goal of the feature therefore is to allow C# programs without unnecessary boilerplate around them, for the sake of learners and the clarity of code.
So what is this new Top-level statement feature included in the next version of the C#.
Top-level statements is a sequence of statements which occur right before the namespace member declarations of a compilation unit (i.e. source file).
Likewise, as there can only be one entry Main() method per application, there can only be one compilation unit with Top-level statements. Any other case will result in compilation error. To put it simple (but technically maybe not the most accurate), top-level statements are silently wrapped inside Main() method generate at compile time. We will see some examples later on in this blog post.
In practice this means that I can replace this boilerplate code:
static void Main(string args)
Console.WriteLine("Hello World from jenx.si!");
with this one:
System.Console.WriteLine("Hello World from jenx.si!");
Let’s write some C#
First of all, C# 9 is still in preview. C# 9 is supported in .NET 5.0. That is to say, for my experimentation I needed .NET 5.0 to be installed on my machine. As of this writing .NET 5.0 is in preview and can be downloaded here: https://dotnet.microsoft.com/download/dotnet/5.0.
OK, now that my environment is ready I can start experimenting.
Example 1: Simple
Console.WriteLine() console app.
Let’s take a look at my decompilation code:
Pretty straightforward, right?
Main()method is silently created and top-level statement are contained inside.
Example 2: Accessing console input parameters.
Decompilation shows similar situation as above.
Example 3: Top-level statements position and class declaration
Warning clearly says that top-level statement should be after using declaration block and before first type/namespace (e.g. class) declaration.
All is fine when I move top-level statement before class declaration.
Decompile clearly shows auto-generated
Main() method and internal class named Test.
Example 4: Adding some complexity to the app.
Generated code is now more advanced, because I introduced async call as a top-level statementc. Nevertheless,
Main() method is generated and top-level statements are handled as expected by compiler.
To be clear, for me this feature is not something I will use frequently. I am just ok with standard boilerplate template. I just love (M/m)ain as a C/C++/C# application entry point.
Nevertheless, I find this new language feature very useful for testing algorithms or parts of my code. Frequently, I need to quickly test part of the code. With this feature I can simply copy paste code into file or some online testing app, like sharplab.io, update the code and paste it back to my app. No namespaces, no main method no default class declaration – just my “problematic” code.
Top-level statements is a nice addition to the C# language. It simplifies the code and reduce the default boilerplate code. It is useful, good-to-have feature, but to be honest, it’s not really a killer feature.
In my opinion, this feature is useful for simple apps, scripting and education purposes.
My personal preferences are: I will use this feature for learning, prototyping, algorithm testing and for proof-of-concept apps. However, with production code I will stick with old fashioned default boilerplate template with
Main() entry method. This way, application entry is more explicit, more object-oriented and more obvious.