当前位置:编程学习 > C#/ASP.NET >>

CodeDom:语言的界限在这里消失

答案:     .NET推崇这样一种思想:相对于框架而言,语言处于从属、次要的地位。CodeDom名称空间中包含的类是这一思想的集中体现。我们可以用CodeDom构造一个树或图,用System.CodeDom名称空间的类填充它,完成后,用对应各种.NET语言的CodeProvider对象将树结构转换成该种语言的代码。要更换一种语言,简单到只需更换一下最后用到的CodeProvider对象。
  
    设想一下,利用这一技术,我们至少能够:
  
    ·查询存储过程的元数据,构造出一个负责参数绑定的类。
  
    ·查询程序集的manifest,构造出一个对每个函数执行单元测试的类。
  
    ·为开发组用到的每一种语言生成样板代码。
  
    ·只需写一份范例代码,就可以让用户自由选择他们要查看哪一种语言的版本。
  
    ·自定义模板语法,经解析后生成任意语言的代码。
  
    ·如果要学习某种不熟悉的语言,可以生成该语言的代码,然后将它与熟悉的语言比较。
  
    一、基本操作
  
    System.CodeDom名称空间包含了许多以语言中立的形式描述常见程序结构的对象,每一种语言的细节则由与该种语言对应的CodeProvider对象负责处理。例如,CodeConditionStatement包含一个TrueStatements集合、一个FalseStatements集合和一个条件属性(Condition attribute),但不涉及条件语句块要用“end if”还是右花括号“}”结束,这部分细节由CodeProvider处理。有了这一层抽象,我们就可以描述待生成的代码结构,然后将它以任意语言的形式输出,却不必斤斤计较于各种与特定语言有关的细节问题。同时,这种抽象也为我们通过程序改变代码的结构带来了方便。例如,当我们发现某个方法需要增加一个参数时,就可以将参数加入到该方法的Parameters集合,根本无须改动已生成的代码逻辑。
  
    我们在本文中要用到的大部分对象来自System.CodeDom名称空间,其余对象主要来自各个与特定语言有关的名称空间,例如Microsoft.CSharp名称空间、Microsoft.VisualBasic名称空间、Microsoft.JScript名称空间和Microsoft.VJSharp名称空间。所有这些面向特定语言的名称空间都包含各自的CodeProvider对象。最后,System.CodeDom.Complier名称空间定义ICodeGenerator接口,后者用来把生成的代码输出到一个TextWriter对象。
  
    如果我们只要生成一些用于插件或宏的代码片断,可以利用CodeGenerator从Statement、Expression、Type等生成代码。反之,如果我们要生成的是一个完整的文件,则必须从CodeNameSpace对象入手。在本文的例子中,我们将从一个名称空间开始,示范如何加入import语句、声明类、声明方法、声明变量、实现一个循环结构、索引一个数组,最后,我们将这些技术结合起来,得到一个大家都熟悉的程序。
  
    1.1 初始化名称空间
  
    初始化名称空间的代码类似下面这种形式:
  
  private CodeNameSpace InitializeNameSpace(string Name)
  {
   // 初始化CodeNameSpace变量,指定名称空间的名称
   CodeNameSpace CurrentNameSpace = new CodeNamespace (Name);
   // 将一些名称空间加入到要导入的名称空间集合。
   // 各种语言如何导入名称空间的细节由每种语言对应
   // 的CodeProvider分别处理。
   CurrentNameSpace.Imports.Add (new CodeNamespaceImport("System"));
   CurrentNameSpace.Imports.Add (new CodeNamespaceImport("System.Text"));
   return CurrentNameSpace;
  }
  
  
  
    这段代码定义了一个新的名称空间,并导入System和System.Text名称空间。
  
    1.2 创建类
  
    声明一个新类的代码类似下面这种形式:
  
  private CodeTypeDeclaration CreateClass (string Name)
  {
   // 新建一个CodeTypeDeclaration对象,指定要创建的类的名称
   CodeTypeDeclaration ctd = new CodeTypeDeclaration (Name);
   // 指定这个CodeType是一个类,而不是一个枚举变量或struct
   ctd.IsClass = true;
   // 这个类的访问类型是public
   ctd.Attributes = MemberAttributes.Public;
   // 返回新创建的类
   return ctd;
  }
  
  
  
    CreateClass函数新建一个指定名称的类,做好为该类植入方法、属性、事件的准备。
  
    1.3 创建方法
  
    声明一个新函数的代码类似下面这种形式:
  
  private CodeEntryPointMethod CreateMethod()
  {
   // 创建一个方法
   CodeEntryPointMethod method = new CodeEntryPointMethod();
   // 指定该方法的修饰符:public,static
   method.Attributes = MemberAttributes.Public |
   MemberAttributes.Static;
   // 返回新创建的方法
   return method;
  }
  
  
  
    本例创建了一个CodeEntryPointMethod对象。CodeEntryPointMethod对象类似于CodeMemberMethod对象,两者的不同之处在于,CodeProvider会将CodeEntryPointMethod代表的方法作为类的入口点调用,例如作为Sub Main或void main等。对于CodeEntryPointMethod对象,方法的名称默认为Main;对于CodeMemberMethod,方法的名称必须显式指定。
  
    1.4 声明变量
  
    声明一个变量的代码类似下面这种形式:
  
  private CodeVariableDeclarationStatement
   DeclareVariables(System.Type DataType,
   string Name)
  {
   // 为将要创建的变量类型创建一个CodeTypeReference对象,
   // 这使得我们不必去关注该类数据在特定语言环境中的
   // 与数据类型有关的细节问题。
   CodeTypeReference tr = new CodeTypeReference (DataType );
   // CodeVariableDeclarationStatement对象使得我们不必纠缠于
   // 与特定语言有关的下列细节:在该语言的变量声明语句中,
   // 应该是数据类型在前,还是变量名称在前;声明变量时是
   // 否要用到Dim之类的关键词.
   CodeVariableDeclarationStatement Declaration =
   new CodeVariableDeclarationStatement(tr, Name);
   // CodeObjectCreateExpression负责处理所有调用构造器的细节。
   // 大多数情况下应该是new,但有时要使用New。但不管怎样,
   // 我们不必去关注这些由语言类型决定的细节.
   CodeObjectCreateExpression newStatement = new
   CodeObjectCreateExpression ();
   // 指定我们要调用其构造器的对象.
   newStatement.CreateType = tr;
   // 变量将通过调用其构造器的方式初始化.
   Declaration.InitExpression = newStatement;
   return Declaration;
  }
  
  
  
    每一种.NET语言都有其特定的数据类型名称,所有这些数据类型都被映射到公共的.NET语言类型。例如,对于C#中称为int的数据类型,在VB.NET中是Integer,公共的.NET类型是System.Int32。CodeTypeReference对象直接使用.NET公共数据类型,以后由每种语言的CodeProvider将它转换成符合各自语言规范的类型名称。
  
    1.5 初始化数组
  
    初始化一个数组的代码类似下面这种形式:
  
  private void InitializeArray (string Name,
   params char[] Characters )
  {
   // 从参数中传入的字符数组获得一个CodeTypeReference 对象,
   // 以便在生成的代码中复制该数据类型.
   CodeTypeReference tr = new CodeTypeReference (Characters.GetType());
   // 声明一个匹配原始数组的数组
   CodeVariableDeclarationStatement Declaration =
   new CodeVariableDeclarationStatement (tr, Name);
   // CodePrimitiveExpression代表“基本”或值数据类型,
   // 例如char、int、double等等。
   // 我们将用这类基本数据类型构成的一个数组来
   // 初始化我们正在

上一个:在 C# 中加载自己编写的动态链接库
下一个:C# Inner Class vs. java 的inner class比较

CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,