生成以后是编译了。在生成的时候,从GenerateCodeFromCompileUnit()这个方法开始,ICodeGenerator里面的其他几个方法都可以被调用到;而在ICodeCompiler里,接口里的方法最后都会调用到这么一个FromFileBatch()方法里来。那么看看在CodeCompiler这个类里是如何实现这个方法的:

protected virtual CompilerResults FromFileBatch(CompilerParameters options, string[] fileNames)
{
…….//前面一些处理略
string text3 = this.CmdArgsFromParameters(options) + " " + CodeCompiler.JoinStringArray(fileNames, " ");
string text4 = this.GetResponseFileCmdArgs(options, text3);
string text5 = null;

if (text4 != null)
{
text5 = text3;
text3 = text4;
}
this.Compile(options, Executor.GetRuntimeInstallDirectory(), this.CompilerName, text3, ref text1, ref num1, text5);
……//下面为编译结果的处理,略
}
问题在this.Compile()这里了,再寻根问底下去:

internal void Compile(CompilerParameters options, string compilerDirectory, string compilerExe, string arguments, ref string outputFile, ref int nativeReturnValue, string trueArgs)
{
……
string text2 = compilerDirectory + compilerExe;
……
nativeReturnValue = Executor.ExecWaitWithCapture(options.UserToken, "\"" + text2 + "\" " + arguments, options.TempFiles, ref outputFile, ref text1, text3);
……
}
出来一个新东西Executor,执行者?就是这个东西,调用命令行编译就是它来做的,而CodeCompiler只是提供出一个命令行字符串而已。Executor是System.CodeDom.Compiler里公开的sealed类,里面全是一些静态方法,追呀追呀追,最后到了这里:

private static int ExecWaitWithCaptureUnimpersonated(IntPtr userToken, string cmd, string currentDir, TempFileCollection tempFiles, ref string outputName, ref string errorName, string trueCmdLine)
{
…….
flag1 = UnsafeNativeMethods.CreateProcess(null, new StringBuilder(cmd), null, null, true, 0, new HandleRef(null, ptr4), currentDir, startupinfo1, process_information1);
…….
int num2 = SafeNativeMethods.WaitForSingleObject(new HandleRef(null, process_information1.hProcess), 600000);
…….

if (!UnsafeNativeMethods.GetExitCodeProcess(new HandleRef(null, process_information1.hProcess), ref num3))
{
……
}
return num3;
…….
}
好熟悉的东西呀,什么CreateProcess、WaitForSingleObject。跑到SafeNativeMethods(或UnsafeNativeMethods)那边一瞧,不得了,熟悉的面孔一大堆,好多的[DllImport()]。原来在.NET下调用命令行没什么新着呀。
上面的东西跟好象跟Provider一点关系都没有。不过也不对呀,编译的时候它是如何找到编译器的呢?Provider肯定在这里起作用的喽。就是this.Compile()那个方法的第三个参数:this.CompilerName,这是一个抽象的属性,具体的值在相关的Provider里提供。注意一下CSharpCodeGenerator这个类,它是从CodeCompiler继承的。这里有个比较有趣的继承关系:
public abstract class CodeGenerator : ICodeGenerator
public abstract class CodeCompiler : CodeGenerator, ICodeCompiler
internal class CSharpCodeGenerator : CodeCompiler
倒不是继承关系有趣,而是名字取得比较的趣,CSharpCodeGenerator这个类原来把生成与编译功能的都包括在里面了,上面那个CompilerName的属性值就是在CSharpCodeGenerator里提供的,好象有点乱(看来看去总觉得当初CodeCompiler从CodeGenerator继承是为了后面CSharpCodeGenerator准备的﹐在CodeCompiler里根本是没改写过CodeGenerator什幺东西﹐一股脑就丢给CSharpCodeGenerator了)。刚开始没注意到这个继承关系,而且也没注意到CSharpCodeGenerator里有CompilerName这样的属性值(藏得太下面了,要点两下滚动条才出来),对它的值是如何得到的百思不得其解。
编译的事也搞定。
System.CodeDom.Compiler里三大接口去其二,还有一个ICodeParser没人管,MS自己也没有提供相应的实现的,在网上倒是有看到过一个。很吓人的东西,复杂的字符串分析,整段整段的switch。描了两眼,还是没有跳进去。
CodeDOM分析就此结束,看下来一点奥秘都没有,否则也不叫浅析了。其实奥秘都在省略的那些东西里,那些才是见真功夫的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现