The evolution of the C# language has introduced us to many new features and probably made a lot of folks re-evaluate how they write their apps. In the latest versions of .NET, nothing has been marked such a stylistic shift as the introduction of top-level statements.
In this short post, we’ll examine how to add properties to your Program
instance to improve the readability of utility console applications.
Top-level statement files
When starting a new console application, you can create a Program.cs
file and opt into the top-level statements style. The single line below is a valid .NET application.
Console.WriteLine("Hello, World");
At compile-time, the compiler generates the typical ceremony associated with traditional applications. Looking at our app’s low-level C# version, we’ll see the symbols we typically expect to see in a .NET app.
using System;
using System.Runtime.CompilerServices;
[CompilerGenerated]
internal class Program
{
private static void <Main>$(string[] args)
{
Console.WriteLine("Hello, World");
}
public Program()
{
base..ctor();
}
}
You’ll see that there is always a Program
class and the expected entry point of static void Main
.
Let’s get more complex.
Console.WriteLine($"Hello, {GetName()}");
string GetName()
{
return Environment.GetCommandLineArgs()[1];
}
What does this look like now?
using System;
using System.Runtime.CompilerServices;
[CompilerGenerated]
internal class Program
{
private static void <Main>$(string[] args)
{
Console.WriteLine(string.Concat("Hello, ", Program.<<Main>$>g__GetName|0_0()));
}
public Program()
{
base..ctor();
}
[NullableContext(1)]
[CompilerGenerated]
internal static string <<Main>$>g__GetName|0_0()
{
return Environment.GetCommandLineArgs()[1];
}
}
The method attached to the end of the file becomes an internal static method on the Program. Can we add a property? In this case, a property would be much more readable than a method.
// not valid c#
Console.WriteLine($"Hello, {Name}");
string Name => Environment.GetCommandLineArgs()[1];
This syntax does not work. Still, we can add a Name
property to our Program
file.
Adding the property with a partial class
How do we get top-level properties into our top-level statements file? Well, it’s pretty straightforward. The Program
class can be extended using the partial
keyword. Let’s do that.
Console.WriteLine($"Hello, {Name}");
partial class Program
{
static string Name => Environment.GetCommandLineArgs()[1];
}
Now, we can add scoped properties to our top-level statement files. Returning to our low-level C#, we can see what the compiler is doing.
using System;
using System.Runtime.CompilerServices;
internal class Program
{
private static void <Main>$(string[] args)
{
Console.WriteLine(string.Concat("Hello, ", Program.Name));
}
[Nullable(1)]
private static string Name
{
[NullableContext(1)] get
{
return Environment.GetCommandLineArgs()[1];
}
}
public Program()
{
base..ctor();
}
}
You can also move your partial Program
class to another file if you are going for that minimal aesthetic entry point look. One thing to note is that all properties must be static
, as the properties are referenced in the static void Main
method.
I hope this post helps, and as always, thanks for reading. Cheers.