Net有道

紫冠道人的求道历程

导航

2004年11月29日 #

CodeDOM浅析(下)

 

生成以后是编译了。在生成的时候,从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只是提供出一个命令行字符串而已。ExecutorSystem.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(nullnew StringBuilder(cmd), nullnulltrue0new 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;
      …….
 }

 

好熟悉的东西呀,什么CreateProcessWaitForSingleObject。跑到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里提供的,好象有点乱(看来看去总觉得当初CodeCompilerCodeGenerator继承是为了后面CSharpCodeGenerator准备的﹐在CodeCompiler里根本是没改写过CodeGenerator什幺东西﹐一股脑就丢给CSharpCodeGenerator)。刚开始没注意到这个继承关系,而且也没注意到CSharpCodeGenerator里有CompilerName这样的属性值(藏得太下面了,要点两下滚动条才出来),对它的值是如何得到的百思不得其解。

    编译的事也搞定。

 

System.CodeDom.Compiler里三大接口去其二,还有一个ICodeParser没人管,MS自己也没有提供相应的实现的,在网上倒是有看到过一个。很吓人的东西,复杂的字符串分析,整段整段的switch。描了两眼,还是没有跳进去。

 

CodeDOM分析就此结束,看下来一点奥秘都没有,否则也不叫浅析了。其实奥秘都在省略的那些东西里,那些才是见真功夫的。

posted @ 2004-11-29 11:44 lichdr 阅读(3993) 评论(10) 编辑

CodeDOM浅析(上)

 

CodeDOM用得差不多了,不能天天只管写代码写呀写的,闲下来之余总是要想一想它是如何来实现这样的功能的呢?就那样建立起来一个CodeDOM的类,然后就可以去生成我们希望的代码,且可以动态的去编译它。

MS的东西没有源代码,不过现在可以用Reflector这样的好工具来粗粗地探究一下里面到底是如何干的(当然如果IL过关的话,也可以直接用Ildasm来看)。这一点还真是不赖。

 

提到CodeDOM实际上是讲.NET Farmework下的System.CodeDomSystem.CodeDom.Compiler这两个命名空间。

CodeDOM的中文全名就是“代码文档对象模型”。要知道模型这个东西其实是很虚的,它基本是不干实事。它那里面那么多的类,从最基本的ExpressionStatement开始,一点点的堆起来,最后得到一个表示NamespaceCompileUnit类,归根结底在System.CodeDom这个命名空间里你newnew去只能是得到一些Object而已,而Object是什么呢,就是内存里的一点点数据,我们根本看不见摸不着(否则也用不着去O/R Mapping),只有通过System.CodeDom.Compiler这个命名空间里的东西才能把它表现出来。前一个命名空间在于构造,后一个命名空间在于表现。构造就是搭个架子,把里面的各个部分聚合聚合,连接连接,这个一点点奥秘都没有,所有也不去深究了。

(说起DOM,想起了XML里的那个DOM,那个东西也跟这相差不多,不过XML里面的东西比Code要复杂多了。)

 

打开System.CodeDom.Compiler这个命名空间,里面的成员比起System.CodeDom来是少得多了,不过虽然少,但来得实在,这里的东西是很实的,个个都很管用。

前面讲《动态生成与编译》的时候提到过了,这里主要有三大接口:ICodeCompilerICodeGeneratorICodeParser以及创建这些接口实现类用的CodeDomProvider。能够用同一份CodeDOM来生成不同程序语言的代码及编译生成的代码,实现这样的一种功能的关键所在其实就是在Provider这里。CodeDomProvider是个抽象类,在.NET 任何某一种程序语言如果要实现动态生成与编译的功能,就要提供一个相应的Provider。如C#CSharpCodeProviderVB.NETVBCodeProvider(Delphi2005也提供了CodeDOM的功能,所以它也有相应的Provider)

下面的只以C#为例。CSharpCodeProvider这个类在Microsoft.CSharp这个命名空间

 

先来生成的,看看ICodeGenerator接口的GenerateCodeFromCompileUnit()方法实现(接口中的其他方法从这里都会调用到)

抽象类public abstract class CodeGenerator :ICodeGenerator 里提供了接口的实现:
void ICodeGenerator.GenerateCodeFromCompileUnit(CodeCompileUnit e, TextWriter w, CodeGeneratorOptions o){
    ……
//一些异常处理。及根据w,o这两个参数设置相应的参数
    ……                  
   
this.GenerateCompileUnit(e);    
    ……
}
同一个类里
protected virtual void GenerateCompileUnit(CodeCompileUnit e){
   
this.GenerateCompileUnitStart(e);        
   
this.GenerateNamespaces(e);  
   
this.GenerateCompileUnitEnd(e);
}

剥掉了一层,从CompileUnit到了Namespace.

 

protected void GenerateNamespaces(CodeCompileUnit e){
    
foreach (CodeNamespace namespace1 in e.Namespaces){
        ((ICodeGenerator) 
this).GenerateCodeFromNamespace(namespace1, this.output.InnerWriter, this.options);
      }

  }

 

void ICodeGenerator.GenerateCodeFromNamespace(CodeNamespace e, TextWriter w, CodeGeneratorOptions o){
   ……
   
this.GenerateNamespace(e);
   ……
}

 

下面再剥一层,把Type露出来了。

 

protected virtual void GenerateNamespace(CodeNamespace e){
      
this.GenerateCommentStatements(e.Comments);
      
this.GenerateNamespaceStart(e);
      
this.GenerateNamespaceImports(e);
      
this.Output.WriteLine("");
      
this.GenerateTypes(e);
      
this.GenerateNamespaceEnd(e);
}

 

再往下当然就是Type里的成员的事了,下面的再略。

实际也可以想出来,代码生成的时候就是根据CodeDOM提供的那个模型,一层层的分解成一句句的具体语句,这与产生CodeDOM时一层层的往上Add的过程恰好是相反。

 

打太极拳打到了现在,还没看到一句产生程序代码的语句呢! 下面就来了,再细细地看一下CodeGenerator这个类里的成员,好多的Generate方法,只要System.CodeDom里有的这里都有对应的一个Generate方法(这是必然喽),不过这些大多是抽象方法呀。因为产生特定的代码是要因语言而异的,所以这些抽象方法的具体实现肯定是在相应的Provider里了

 

Microsoft.CSharp里有一个internal class CSharpCodeGenerator : CodeCompiler ProviderCreateGenerator()CreateCompiler()这两个方法返回的就是上面的这个internal类。看看CSharpCodeGenerator里有些什么?一大堆的Generate的方法。没错了,就是它,生成C#代码过程就是在这里完成的。

随便点一个看看就明白是如何产生代码的,下面这个产生赋值语句的简单点:

 

protected override void GenerateAssignStatement(CodeAssignStatement e){
      
base.GenerateExpression(e.Left);
      
base.Output.Write(" = ");
      
base.GenerateExpression(e.Right);
      
if (!this.forLoopHack)      {
            
base.Output.WriteLine(";");
      }

}

 

生成左边的,写入一个“=”号,然后生成右边的,最后写一个“;”(如果在for循环头部里的赋值语句就特殊点,不要这个“;”了)Output是什么?就是根据接口方法GenerateCodeFromCompileUnit()刚开始传进来的那个TextWriter参数设置的属性。再看几个Generate方法,能看到很多的base.Output.Write()这样的语句。

很明白了,Generate过程就是从上往下层层分解得到ExpressionStatement,一字符串一字符串的往TextWriter里写的。

所以如果你要的动态程序不需要很灵活(用少数几个变量,再加点条件判断就能搞定的那种),而且又用不着多语言的话,大可不必用CodeDOM,还是自己搞个TextWriter来写省事又省心

 

posted @ 2004-11-29 11:40 lichdr 阅读(5938) 评论(2) 编辑