6

Top-level programs in C# 9.0

 3 years ago
source link: https://gunnarpeipman.com/csharp-top-level-programs/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Top-level programs in C# 9.0

C# 9.0 comes with nice new feature called top-level programs. It’s something that teachers of beginner classes will love for sure. Imagine – you start teaching C# with only two lines of code on screen. All missing code is generated by compiler. This blog post introduces top-level programs and shows some secrets of new C# compiler.

Classic console application

Here’s the classic console application. We have seen it thousands of times and we know what it does and how it works.

using System;

namespace ConsoleApp6
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

When students see this they usually have more than one question:

  1. What is doing “using System”?
  2. What is “namespace”?
  3. What is string “string[] args”?

Besides question there’s too much noice for them and all work on whiteboard happens inside Main() method.

What is top-level program?

With C# 9 we can skip all the noise and use top-level programs. Same code with this new feature looks like this.

using System;

Console.WriteLine("Hello World!");

Just two lines, no spacing and all the screen space is for my examples. Great!

Of course, I must have using directives but here’s the little trick to calm youngsters down.

using System; // let's talk about it later

Console.WriteLine("Hello World!");

Not only for teachers. Top-level programs can be used also in practice when writing real code. It is perfect when writing simple utility applications that doesn’t have much code.

Top-level programs are C# language feature and it doesn’t come down to Common Language Runtime (CLR). C# compiler produces Program class and Main() method (almost) like it was before.

But what about args argument of Main() method? It’s magically here and available in top-level programs. Here’s how to write out all arguments of program.

for(var i = 0; i < args.Length; i++)
{
    Console.Write(i);
    Console.Write(" ");
    Console.WriteLine(args[i]);
}

Using methods in top-level programs

We can write methods too. Notice that methods in top-level programs must be static.

using System; // let's talk about it later

Console.WriteLine(SayHello("students"));

static string SayHello(string name)
{
    return "Hello, " + name;
}

It’s interesting to see how methods in top-level programs look like functions in functional languages.

Using classes in top-level programs

We can also use classes and other things like structures, enums etc. The next code sample demonstrates primitive greeter class.

using System; // let's talk about it later

var greeter = new Greeter();

var helloTeacher = greeter.Greet("teacher");
var helloStudents = SayHello("students");

Console.WriteLine(helloTeacher);
Console.WriteLine(helloStudents);

static string SayHello(string name)
{
    return "Hello, " + name;
}

public class Greeter
{
    public string Greet(string name)
    {
        return "Hello, " + name;
    }
}

In this point things may get a little bit messy as there are methods and classes coming one after another.

Order matters! In top-level programs type definitions must come after top-level statements. It means after everything that goes to Program class.

Top-level programs after compiling

Classes in top-level programs are not particularly interesting as they are compiled like usual. But things get interesting with Program class and Main() method. Here’s the decompiled code of sample with greeter class.

using System;
using System.Runtime.CompilerServices;

[CompilerGenerated]
internal static class <Program>$
{
    private static void <Main>$(string[] args)
    {
        string str1 = new Greeter().Greet("teacher");
        string str2 = <Program>$.<<Main>$>g__SayHello|0_0("students");
        Console.WriteLine(str1);
        Console.WriteLine(str2);
    }

internal static string <<Main>$>g__SayHello|0_0(string name)
    {
        return "Hello, " + name;
    }
}

Important things to notice:

  • Program class has CompilerGenerated attribute
  • Program class, Main() method and SayHello() method have obfuscated names that make it impossible to refer to them in .NET languages.
  • All top-level code members are in private or internal scope and invisible to other libraries.

Of course, it doesn’t stop us using obfuscated names if we use reflection. But I really don’t see any use case where we would need it and where we really cannot use some other approach.

Wrapping up

Top-level programs is nice C# language feature that cleans up Main() method of Program class by generating class and method automatically. All we have to do is to write the code and build it. Top-level programs have different Program class and Main() method as these are compiler generated. These two are not directly accessible from other libraries although we can do everything we want with reflection. Practical use cases for top-level programs are teaching coding to beginners and writing simple utility applications.

Liked this post? Empower your friends by sharing it!

A portal focused on Operations and Support for Microsoft Azure Serverless services

FREE TRIAL

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK