如何更改C#控制台应用程序的入口点?

5

我想知道是否有可能将下面示例中 .NET 控制台应用程序的入口点从 Main 更改为 Main2 方法:

class Program
{ 
    static void Main(string[] args)
    {
        Console.WriteLine("Main");
    }

    //desired entry point
    static void Main2(string[] args)
    {
        Console.WriteLine("Main2");
    }
}

我调查了这两个的IL代码。以下是Main方法:

我调查了这两个的IL代码。以下是Main方法:

  .method private hidebysig static void 
    Main(
      string[] args
    ) cil managed 
  {
    .entrypoint
    .maxstack 8

    // other instructions

  } // end of method Program::Main

而且 Main2 方法:

.method private hidebysig static void 
    Main2(
      string[] args
    ) cil managed 
  {
    .maxstack 8

    //other instructions
  } // end of method Program::Main2

唯一的区别是Main方法中存在.entrypoint指令,据我所知,当应用程序启动时,CLR会检测到这一点。
有没有办法影响csc以标记其他方法?其他编译器能否做到这一点? 编辑 我的问题与这个问题不同,因为我询问编译器(和其他编译器)的行为...具体如何将.entrypoint指令放在其他位置。

@Caramiriel 是的,即使使用Visual Studio也可以完成这个操作。我的问题更多是关于是否有可能将一个带有不同签名的方法标记为入口点。 - Artem
你能提供更多的上下文吗?你是想在不必后处理生成的代码的情况下完成这个任务吗?使用Main2而不是Main目的是什么?目前这感觉像一个XY问题。 - Jon Skeet
Caramiriel的评论似乎已被删除,所以如果我问了同样的问题,我很抱歉:您是出于好奇还是有实际原因?因为在语言内部有其他方法可以实现 - 根据您要做什么,您可以从Main中调用Main2,或者使用预处理器符号(条件编译),或使用Richard答案中提到的-main选项,甚至可以通过根据参数从Main调用不同的方法来在运行时完成。 - Filip Milovanović
@FilipMilovanović 我目前没有实际兴趣,只是好奇。 - Artem
@Caramiriel 我会检查一下,谢谢。 - Artem
显示剩余4条评论
4个回答

4
这可以通过使用AssemblyBuilder和来自System.Reflection库的其他工具实现。
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"),AssemblyBuilderAccess.Save);
TypeBuilder typeBuilder = assemblyBuilder.DefineDynamicModule("Module","Test.exe",false).DefineType("Program",TypeAttributes.Public);
MethodBuilder methodBuilder = typeBuilder.DefineMethod("Main2",MethodAttributes.Public|MethodAttributes.Static);
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.EmitWriteLine("Main2");
ilGenerator.Emit(OpCodes.Ret);
assemblyBuilder.SetEntryPoint(methodBuilder);
typeBuilder.CreateType();
assemblyBuilder.Save("Test.exe");

这会生成以下IL代码(.entryPoint被放置在Main2方法上):

.method public static 
void Main2 () cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 11 (0xb)
    .maxstack 1
    .entrypoint

    IL_0000: ldstr "Main2"
    IL_0005: call void [mscorlib]System.Console::WriteLine(string)
    IL_000a: ret
} // end of method Program::Main2

如果您执行Test.exe,您会看到执行Main2方法。

当然可以,但是你特别询问了使用 csc.exe 进行操作。在这个代码示例中,你正在使用 AssemblyBuilder - Lucas

2

按照C#语言规范§3.1的规定,入口点必须命名为Main

3.1 Application Startup

Application startup occurs when the execution environment calls a designated method, which is referred to as the application's entry point. This entry point method is always named Main, and can have one of the following signatures:

static void Main() {...}
static void Main(string[] args) {...}
static int Main() {...}
static int Main(string[] args) {...}

看到“总是被命名为Main”这句话了吗?如果你通过编写另一个编译器将入口点更改为除Main之外的其他内容,那么该编译器就不符合C#编译器的定义。 :)


3
还有 static async Task Main(string[] args) ;) - TheGeneral
@TheGeneral 我想知道为什么规范上没有那个签名...我引用的规范是5.0版。async在那时肯定存在。 - Sweeper
1
我认为支持它的是C# 7.x版本,而且我认为官方规范尚未公开。 - TheGeneral

2
根据定义,这是不可能的。在C#应用程序中,Main方法是入口点。

Main方法是C#应用程序的入口点。(库和服务不需要Main方法作为入口点。)当应用程序启动时,将首先调用Main方法。

来源:MSDN

1
该方法必须被称为Main,但是您可以使用-main选项来指定哪个类的Main方法是入口点。
(将Main用作入口点是C#的定义的一部分-在C# v5规范中它在§3.1中,您可能能够克服这一点,但您将自行承担风险。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接