How to call Program.Main method if you're using top-level statements
#csharpLately, I’ve been writing a lot of services that use top-level statements. The code with this kind of syntax sugar simplifies the Main
method and improves readability. However, on the other hand, it makes it more difficult to create integration tests for the services. There isn’t simply way to call the Main
method and pass the necessary arguments.
Since Microsoft introduced top-level statements in C# 9, you no longer need to write a weird Java-like public static void Main(string[] args)
method. So the code
using System;
namespace Application
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine($"args: {string.Join(",", args)}");
}
}
}
can be written just in one line:
System.Console.WriteLine($"args: {string.Join(",", args)}");
But what if we want to call this code and pass some arguments? By default, the C# compiler will convert the code above to the following code:
internal class Program
{
private static void <Main>$(string[] args)
{
Console.WriteLine(string.Concat("args: ", string.Join(",", args)));
}
}
The class is internal, and the method is private. You cannot call the <Main>
method directly, especially from another assembly. How can you overcome this?
First of all, you should declare the Program
class as public partial
. This makes the Program
class visible in external code. If you can’t change the code, you can load the type from the assembly using Assembly.GetType.
System.Console.WriteLine($"args: {string.Join(",", args)}");
public partial class Program { }
Now that the Program
class has become visible from other assemblies, the <Main>
method is still private. Using reflection, we can retrieve the <Main>
method from the DeclaredMethods
collection and call it using Invoke
:
var typeInfo = typeof(Program) as TypeInfo ?? throw new InvalidOperationException();
var main = typeInfo.DeclaredMethods.First(m => m.Name == "<Main>");
var args = new[] { "arg1", "arg2" };
main.Invoke(null, new object[] { args });
That’s it. Reflection provides a powerful mechanism which helps us with writing tests for programs that use top-level statements.