技术开发 频道

VS集成之代码生成机制实现强类型编程

  二、通过CodeDom实现动态代码生成

  CodeDOM 提供了表示许多常见的源代码元素类型的类型。您可以设计一个生成源代码模型的程序,使用CodeDOM 元素构成一个对象图。而这个对象图包含C#或者VB.NET代码包含的基本元素:命名空间、类型、类型成员(方法、属性、构造函数、事件等),并且包括方法实现的具体语句(Statement)。也就是说它的结构就是对一个具体.vb或者.cs文件代码的反映。在这里我不会具体介绍CodeDOM体系结构,有兴趣的读者可以参与MSDN官方文档。

  CodeDOM最终体现出来的是一个叫做CodeCompileUnit对象,这个对象通过如下定义的MessageCodeGnerator的BuildCodeObject方法返回。下面给出了生成CodeCompileUnit的全部实现,即使你对CodeDOM完全不了解,结合上面给出的保存消息的XML和我们最终期望的C#代码的结构,相信也能够看懂整个实现逻辑。

  总的来说,BuildCodeObject方法的目的就是一个将XML转换成CodeCompileUnit对象。首先在BuildCodeObject方法中,添加了一个命名空间(Artech.CodeDomGenerator),并在该命名空间中定义了一个Messages的类。在Messages类会为每一个消息类别定义一个嵌套类,类型的名称就是消息类别的名称(比如Validation、Confirmation等)。我们具体的MessageEntry通过公共静态属性的形式进行定义,并且采用Inline的方式进行初始化。

public class MessageCodeGenerator  
{  
    
public CodeCompileUnit BuildCodeObject(XmlDocument messages)  
     {  
         var codeObject
= new CodeCompileUnit();  
         var codeNamespace
= new CodeNamespace("Artech.CodeDomGenerator");  
         codeObject.Namespaces.Add(codeNamespace);  
         var codeType
= new CodeTypeDeclaration("Messages");  
         codeNamespace.Types.Add(codeType);  
         GenerateCatetoryClasses(codeType, messages);  
         return codeObject;  
     }        
    
    
private void GenerateCatetoryClasses(CodeTypeDeclaration root, XmlDocument messageDoc)  
     {  
         var messageEntries
= messageDoc.GetElementsByTagName("message").Cast<XmlElement>();  
         var categories
= (from element in messageEntries  
                          
select element.Attributes["category"].Value).Distinct();  
    
         foreach (var category in categories)  
         {  
             var categoryType
= new CodeTypeDeclaration(category);  
             root.Members.Add(categoryType);  
    
             foreach (var element in messageDoc.GetElementsByTagName(
"message").Cast<XmlElement>().  
                 Where(element
=> element.Attributes["category"].Value == category))  
             {  
                 GenerateMessageProperty(element, categoryType);  
             }  
         }  
     }  
    
    
private void GenerateMessageProperty(XmlElement messageEntry, CodeTypeDeclaration type)  
     {  
        
string id           = messageEntry.Attributes["id"].Value;  
        
string value        = messageEntry.Attributes["value"].Value;  
        
string categotry    = messageEntry.Attributes["category"].Value;  
    
         var field
= new CodeMemberField(typeof(MessageEntry), id);  
         type.Members.Add(field);  
         field.Attributes
= MemberAttributes.Public | MemberAttributes.Static;  
         field.InitExpression
= new CodeObjectCreateExpression(
             typeof(MessageEntry),  
            
new CodePrimitiveExpression(id),  
            
new CodePrimitiveExpression(value),  
            
new CodePrimitiveExpression(categotry));  
     }      
}

 

  三、通过CodeDomProvider转化给予某种语言的代码

  CodeCompileUnit最终体现的代码的结构,但是CodeCompileUnit本身是不基于某种具体的编程语言的,也就是说CodeCompileUnit是语言中性的。最终我们需要另一个对象将CodeCompileUnit转换成基于某种编程的语言的代码:CodeDomProvider。

  在上面的代码中,我们利用上面定义的MessageCodeGenerator类型,将上述我们提到的包含消息定义的XML文件转换成CodeDomProvider对象。最终通过CodeDomProvider将其分别转换成C#代码和VB。NET代码。

var generator = new MessageCodeGenerator();  
var messageDoc
= new XmlDocument();  
messageDoc.Load(
"Messages.xml");  
var codeObject
= generator.BuildCodeObject(messageDoc);  
CodeDomProvider provider
= CodeDomProvider.CreateProvider("CSharp");  
CodeGeneratorOptions options
= new CodeGeneratorOptions();  
using (StreamWriter writer
= new StreamWriter("messages.cs"))  
{  
     provider.GenerateCodeFromCompileUnit(codeObject, writer, options);                
}  
  
provider
= CodeDomProvider.CreateProvider("VisualBasic");  
using (StreamWriter writer
= new StreamWriter("messages.vb"))  
{  
     provider.GenerateCodeFromCompileUnit(codeObject, writer, options);  
}  
    
Process.Start(
"messages.cs");  
Process.Start(
"messages.vb");

  

0
相关文章