为何C#顶层语句的入口点命名为<Main>$而非Main?
核心原因主要有以下几点:
彻底避免命名冲突
顶层语句的设计就是为了简化代码,用户很可能在顶层代码里随手定义各种方法——包括不小心写出一个Main方法。<Main>$这种包含特殊字符的名称,用户几乎不会主动使用,能完全避免和用户自定义的Main重名,编译器可以安全生成真正的入口方法,不会被用户代码干扰。适配多顶层文件的代码合并逻辑
C#允许在多个文件中编写顶层语句,编译器会按文件顺序把所有顶层代码合并到同一个入口方法里执行。如果用普通Main作为入口名,一旦用户在某个文件里定义了Main,就会出现"多个入口方法"的编译冲突。而<Main>$是编译器专属的特殊名称,能保证编译器对合并后的入口逻辑的绝对控制权,不会出现歧义。支持顶层语句的特殊语法特性
比如C# 9引入的顶层await,用户不需要手动把入口方法声明为async Task Main。编译器生成的<Main>$会根据顶层代码是否包含await,自动生成对应的异步/同步方法,处理异步逻辑的调度。如果用普通Main,要么需要用户显式声明async,要么编译器需要修改用户可能存在的Main方法,这显然不符合顶层语句"简洁易用"的设计初衷。明确区分用户代码与编译器生成代码
把编译器自动生成的入口方法命名为<Main>$,能清晰划清用户自己写的Main方法和程序真正入口的界限。当项目中同时存在顶层语句和用户自定义Main时,编译器会优先以<Main>$作为入口,用户的Main只是普通方法,不会引发入口歧义。
你提到的通过throw语句验证的情况——仅包含throw new Exception("foo bar");的顶层程序运行时,异常栈显示入口为Program.<Main>$(String[] args),正是编译器生成的这个特殊入口方法在执行的直接证据。
内容的提问来源于stack exchange,提问作者winscripter

