alexeyfv

Опубликовано

- 1 мин чтения

Как вызвать Program.Main с top-level statements

C#
img of Как вызвать Program.Main с top-level statements

В последнее время я пишу много сервисов, использующих top-level statements. Такой синтаксический сахар упрощает метод Main и делает код читаемее. Но, с другой стороны, это усложняет написание интеграционных тестов для таких сервисов. Просто так вызвать метод Main и передать ему аргументы не получится.

Начиная с версии C# 9, благодаря поддержке top-level statements больше не нужно писать код в стиле Java public static void Main(string[] args). Теперь код

   using System;

namespace Application
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine($"args: {string.Join(",", args)}");
        }
    }
}

можно заменить на одну строку:

   System.Console.WriteLine($"args: {string.Join(",", args)}");

Но как передать аргументы в такой код? По умолчанию компилятор C# преобразует его в следующий код:

   internal class Program
{
    private static void <Main>$(string[] args)
    {
        Console.WriteLine(string.Concat("args: ", string.Join(",", args)));
    }
}

Класс становится internal, а метод — private. Нельзя напрямую вызвать метод <Main>, особенно из другой сборки. Что можно сделать?

Во-первых, нужно объявить класс Program как public partial. Это сделает его видимым для внешнего кода. Если изменить код нельзя, можно загрузить тип из сборки через Assembly.GetType.

   System.Console.WriteLine($"args: {string.Join(",", args)}");

public partial class Program { }

Теперь класс Program становится видимым для других сборок, но метод <Main> всё ещё private. С помощью рефлексии можно найти метод в списке DeclaredMethods и вызвать его через 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 });

Вот и всё. Рефлексия даёт мощный инструмент для написания тестов к программам с top-level кодом.