我不知道大家对CodeDOM的代码生成机制是否熟悉,但是有一点可以确定:如果你使用过Visual Studio,你就应该体验过它带给我们在编程上的便利。随便列举三种典型的代码生成的场景:在创建强类型DataSet的时候,VS会自动根据Schema生成相应的C#或者VB.NET代码;当我们编辑Resource文件的时候,相应的的后台代码也会自动生成;当我们通过添加Web Reference调用Web Service或者WCF Service的时候,VS会自动生成服务代理的代码和相应的配置。总的来说,通过和VS集成的动态代码生成工具使我们可以“强类型”的方式进行编程,进而提供我们的效率并减低错误的几率

实际上,除了VS提供的这些典型的代码生成场景中,我们可以根据需要开发一些自定义代码生成器,并且通过VS的扩展实现后台代码的实时生成,从而实现强类型编程的目的,现在我们举一个典型的应用场景——消息管理。

一、一个典型的自定义代码生成器应用场景——消息管理

无论对于怎么样的应用,我们都需要维护一系列的消息。消息的类型很多,比如验证消息、确认消息、日志消息等。我们一般会将消息储存在一个文件或者数据库中进行维护,并提供一些API来获取相应的消息项。这些API一般都是基于消息的ID来获取的,换句话说,消息获取的方式是以一种“弱类型”的编程方式实现的。如果我们能够根据消息存储的内容动态地生成相应的C#或者VB.NET代码,那么我们就能够以一种强类型的方式来获取相应的消息项了

比如说,现在我们定义了如下一个MessageEntry类型来表示一个消息条目。为了简单,我们尽量简化MessageEntry的定义,仅仅保留三个属性Id、Value和Category。Category表示该消息条目所属的类型,你可以根据具体的需要对其分类(比如根据模块名称或者Severity等)。Value是一个消息真实的内容,可以包含一些占位符({0},{1},…{N})。通过指定占位符对用的值,最中格式化后的文本通过Format返回。

   1: public class MessageEntry
   2: {
   3:     public string Id { get; private set; }
   4:     public string Value { get; private set; }
   5:     public string Category { get; private set; }
   6:     public MessageEntry(string id, string value, string category)
   7:     {
   8:         this.Id         = id;
   9:         this.Value      = value;
  10:         this.Category   = category;
  11:     }
  12:     public string Format(params object[] args)
  13:     {
  14:         return string.Format(this.Value, args);
  15:     }
  16: }

现在我们所有的消息定义在如下一个XML文件中,<message>XML元素代码一个具体的MessageEntry,相应的属性(Attribute)和MessageEntry的属性(Property)相对应。

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <messages>
   3:   <message id="MandatoryField" value="The {0} is mandatory."  category="Validation"/>
   4:   <message id="GreaterThan" value="The {0} must be greater than {1}."  category="Validation"/>
   5:   <message id="ReallyDelete" value="Do you really want to delete the {0}."  category="Confirmation"/>
   6: </messages>

在上面的XML中,定义了两个类别(Validation和Confirmation)的三条MessageEntry。我们需要通过我们的代码生成工具生成一个包含如下C#代码的CS文件

   1: namespace Artech.CodeDomGenerator
   2: {     
   3:     public class Messages
   4:     {        
   5:         public class Validation
   6:         {            
   7:             public static Artech.CodeDomGenerator.MessageEntry MandatoryField = new Artech.CodeDomGenerator.MessageEntry("MandatoryField", "The {0} is mandatory.", "Validation");
   8:             public static Artech.CodeDomGenerator.MessageEntry GreaterThan = new Artech.CodeDomGenerator.MessageEntry("GreaterThan", "The {0} must be greater than {1}.", "Validation");
   9:         }        
  10:         public class Confirmation
  11:         {            
  12:             public static Artech.CodeDomGenerator.MessageEntry ReallyDelete = new Artech.CodeDomGenerator.MessageEntry("ReallyDelete", "Do you really want to delete the {0}.", "Confirmation");
  13:         }
  14:     }
  15: }

那么我们就能够直接通过生成出来的Messages类,以强类型的方式获取并格式化每一条MessageEntry的内容了。

   1: Console.WriteLine(Messages.Validation.MandatoryField.Format("User Name"));
   2: Console.WriteLine(Messages.Validation.GreaterThan.Format("Age",18));
   3: Console.WriteLine(Messages.Confirmation.ReallyDelete.Format("Order record"));

下面是输出结果:

   1: The User Name is mandatory.
   2: The Age must be greater than 18.
   3: Do you really want to delete the Order record.

要实现上面的功能实际上包含两个步骤:一是动态解析包含消息定义的XML文件,并生成我们希望结构的一个代码定义,而是通过和VS进行集成,借助VS自定义工具将前面生成的内容真正写入到一个具体的.cs文件中。第一个步骤可以通过CodeDOM轻松实现,而第二个步骤借助于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的方式进行初始化。

   1: public class MessageCodeGenerator
   2: {
   3:     public CodeCompileUnit BuildCodeObject(XmlDocument messages)
   4:     {
   5:         var codeObject = new CodeCompileUnit();
   6:         var codeNamespace = new CodeNamespace("Artech.CodeDomGenerator");
   7:         codeObject.Namespaces.Add(codeNamespace);
   8:         var codeType = new CodeTypeDeclaration("Messages");
   9:         codeNamespace.Types.Add(codeType);
  10:         GenerateCatetoryClasses(codeType, messages);
  11:         return codeObject;
  12:     }       
  13:  
  14:     private void GenerateCatetoryClasses(CodeTypeDeclaration root, XmlDocument messageDoc)
  15:     {
  16:         var messageEntries = messageDoc.GetElementsByTagName("message").Cast<XmlElement>();
  17:         var categories = (from element in messageEntries
  18:                           select element.Attributes["category"].Value).Distinct();
  19:  
  20:         foreach (var category in categories)
  21:         {
  22:             var categoryType = new CodeTypeDeclaration(category);
  23:             root.Members.Add(categoryType);
  24:  
  25:             foreach (var element in messageDoc.GetElementsByTagName("message").Cast<XmlElement>().
  26:                 Where(element => element.Attributes["category"].Value == category))
  27:             {
  28:                 GenerateMessageProperty(element, categoryType);
  29:             }
  30:         }
  31:     }
  32:  
  33:     private void GenerateMessageProperty(XmlElement messageEntry, CodeTypeDeclaration type)
  34:     {
  35:         string id           = messageEntry.Attributes["id"].Value;
  36:         string value        = messageEntry.Attributes["value"].Value;
  37:         string categotry    = messageEntry.Attributes["category"].Value;
  38:  
  39:         var field = new CodeMemberField(typeof(MessageEntry), id);
  40:         type.Members.Add(field);
  41:         field.Attributes = MemberAttributes.Public | MemberAttributes.Static;
  42:         field.InitExpression = new CodeObjectCreateExpression(
  43:             typeof(MessageEntry),
  44:             new CodePrimitiveExpression(id),
  45:             new CodePrimitiveExpression(value),
  46:             new CodePrimitiveExpression(categotry));
  47:     }    
  48: }

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

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

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

   1: var generator = new MessageCodeGenerator();
   2: var messageDoc = new XmlDocument();
   3: messageDoc.Load("Messages.xml");
   4: var codeObject = generator.BuildCodeObject(messageDoc);
   5: CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
   6: CodeGeneratorOptions options = new CodeGeneratorOptions();
   7: using (StreamWriter writer = new StreamWriter("messages.cs"))
   8: {
   9:     provider.GenerateCodeFromCompileUnit(codeObject, writer, options);               
  10: }
  11:  
  12: provider = CodeDomProvider.CreateProvider("VisualBasic");
  13: using (StreamWriter writer = new StreamWriter("messages.vb"))
  14: {
  15:     provider.GenerateCodeFromCompileUnit(codeObject, writer, options);
  16: }
  17:  
  18: Process.Start("messages.cs");
  19: Process.Start("messages.vb");

这是C#代码(和我们开始提到过的完全一致):

   1: //------------------------------------------------------------------------------
   2: // <auto-generated>
   3: //     This code was generated by a tool.
   4: //     Runtime Version:4.0.30319.1
   5: //
   6: //     Changes to this file may cause incorrect behavior and will be lost if
   7: //     the code is regenerated.
   8: // </auto-generated>
   9: //------------------------------------------------------------------------------
  10:  
  11: namespace Artech.CodeDomGenerator {
  12:     
  13:     
  14:     public class Messages {
  15:         
  16:         public class Validation {
  17:             
  18:             public static Artech.CodeDomGenerator.MessageEntry MandatoryField = new Artech.CodeDomGenerator.MessageEntry("MandatoryField", "The {0} is mandatory.", "Validation");
  19:             
  20:             public static Artech.CodeDomGenerator.MessageEntry GreaterThan = new Artech.CodeDomGenerator.MessageEntry("GreaterThan", "The {0} must be greater than {1}.", "Validation");
  21:         }
  22:         
  23:         public class Confirmation {
  24:             
  25:             public static Artech.CodeDomGenerator.MessageEntry ReallyDelete = new Artech.CodeDomGenerator.MessageEntry("ReallyDelete", "Do you really want to delete the {0}.", "Confirmation");
  26:         }
  27:     }
  28: }

下面是VB.NET代码:

   1: '------------------------------------------------------------------------------
   2: ' <auto-generated>
   3: '     This code was generated by a tool.
   4: '     Runtime Version:4.0.30319.1
   5: '
   6: '     Changes to this file may cause incorrect behavior and will be lost if
   7: '     the code is regenerated.
   8: ' </auto-generated>
   9: '------------------------------------------------------------------------------
  10:  
  11: Option Strict Off
  12: Option Explicit On
  13:  
  14:  
  15: Namespace Artech.CodeDomGenerator
  16:     
  17:     Public Class Messages
  18:         
  19:         Public Class Validation
  20:             
  21:             Public Shared MandatoryField As Artech.CodeDomGenerator.MessageEntry = New Artech.CodeDomGenerator.MessageEntry("MandatoryField", "The {0} is mandatory.", "Validation")
  22:             
  23:             Public Shared GreaterThan As Artech.CodeDomGenerator.MessageEntry = New Artech.CodeDomGenerator.MessageEntry("GreaterThan", "The {0} must be greater than {1}.", "Validation")
  24:         End Class
  25:         
  26:         Public Class Confirmation
  27:             
  28:             Public Shared ReallyDelete As Artech.CodeDomGenerator.MessageEntry = New Artech.CodeDomGenerator.MessageEntry("ReallyDelete", "Do you really want to delete the {0}.", "Confirmation")
  29:         End Class
  30:     End Class
  31: End Namespace

在《下篇》中,我们将着重介绍如果通过VS的扩展实现如何将我们的MessageCodeGenerator和XML进行绑定,使XML内容改变的时候,相应的代码能够动态的生成。

从数据到代码——通过代码生成机制实现强类型编程[上篇]
从数据到代码——通过代码生成机制实现强类型编程[下篇]
从数据到代码——基于T4的代码生成方式
创建代码生成器可以很简单:如何通过T4模板生成代码?[上篇]
创建代码生成器可以很简单:如何通过T4模板生成代码?[下篇]

作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

从数据到代码——通过代码生成机制实现强类型编程[上篇]相关推荐

  1. 通过代码生成机制实现强类型编程-CodeSimth版

    一直想写一个Code生成系列,但写到CodeSimth,发觉在TerryLee 和努力学习的小熊 两位大牛的博客里讲很详尽,所以就像写些示例方面的,但是苦于没有想到写些什么.最近Artech写了两篇从 ...

  2. python使用什么来区分代码块_Python 小数据池、代码块以及代码块缓存机制

    按照惯例,吟诗一首:苏轼<江城子·乙卯正月二十日夜记梦> 十年生死两茫茫,不思量,自难忘.千里孤坟,无处话凄凉. 纵使相逢应不识,尘满面,鬓如霜. 夜来幽梦忽还乡,小轩窗,正梳妆.相顾无言 ...

  3. 采用SpringBoot、MyBatis-Plus 、Security框架,开发的一套权限系统,实现前后端分离,完善的代码生成机制

    项目说明 harry 采用SpringBoot.MyBatis-Plus .Security框架,开发的一套权限系统,实现前后端分离,完善的代码生成机制.Harry Gitee地址 harry-vue ...

  4. Hibernate中的三种数据持久状态和缓存机制

    Java三大框架之--Hibernate中的三种数据持久状态和缓存机制 Hibernate中的三种状态   瞬时状态:刚创建的对象还没有被Session持久化.缓存中不存在这个对象的数据并且数据库中没 ...

  5. 新的云威胁!黑客利用云技术窃取数据和源代码

    新的云威胁!黑客利用云技术窃取数据和源代码 本文作者:Zhuolin, 转载请注明来自FreeBuf.COM Zhuolin 2023-03-01 14:16:53 41733 一个被称为 " ...

  6. 让OpenAI GPT3替我写数据竞赛代码!

    ★★★ 本文源自AlStudio社区精品项目,[点击此处]查看更多精品内容 >>> 让OpenAI GPT3替我写数据竞赛代码! OpenAI与ChatGPT OpenAI是在美国成 ...

  7. 数据治理:数据治理之道-组织机制-敏捷的治理组织

    参考<一本书讲透数据治理>.<数据治理>等 组织机制:敏捷的治理组织 数据.组织.软件平台,是企业数字化转型面临的三座大山 数据:数据是企业数字化转型的根本驱动力之一,数字化转 ...

  8. 汇编语言中将数据、代码、栈放入不同的段

    数据.代码.栈放入不同的段 在学习汇编语言,将数据.代码.栈放入不同的段.参考王爽老师的<汇编语言>第四版,对P133的汇编代码,进行了个人理解标注.仅供参考,存在错误之处,请大家斧正. ...

  9. TensorFlow csv读取文件数据(代码实现)

    TensorFlow csv读取文件数据(代码实现) 大多数人了解 Pandas 及其在处理大数据文件方面的实用性.TensorFlow 提供了读取这种文件的方法. 前面章节中,介绍了如何在 Tens ...

最新文章

  1. Windows 命令行终端 PowerShell 美化计划
  2. python官网怎么改中文-pycharm如何设置成中文
  3. regexpal 正则表达式实时调试工具
  4. 谈谈我自己(创业四个多月)
  5. Storm单节点部署及启动
  6. 如何將Clonezilla live放到一個已經有其他作業系統存在的硬碟中
  7. yuzu模拟器linux,Yuzu Early Acces
  8. leetcode 18 --- 4sum
  9. ansys怎么建立弯曲圆柱_螺栓连接模型的建立方法解析
  10. TortoiseGit使用ssh-keygen生成的私钥
  11. 用python画出圣诞树_【闲趣】如何用python画出一棵圣诞树
  12. domcontentloaded ajax,Ajax优化(1) — DOMContentLoaded
  13. 勾股定理的毕达哥拉斯证明
  14. html 播放微信amr音频文件,微信amr文件打开的方法
  15. STM32F103X hal RTThread rtc驱动支持日期保存
  16. 基于小程序制作一个猜拳小游戏
  17. 超声波模块测距 Arduino代码
  18. C/C++黑魔法-防御性编程
  19. ARM汇编指令(B/BL/BX)
  20. 国家标准《信息技术 人工智能 知识图谱技术框架》第三次编辑会成功召开

热门文章

  1. Liferay7 BPM门户开发之24: Liferay7应用程序安全
  2. qt下调用win32api 修改分辨率
  3. 第8天 NSD NETWOEK
  4. Android用开源优势超Windows Mobile
  5. qt 中 使用 opengl 上下文 (context) 相关的注意事项
  6. java中子类实例化过程中的内存分配
  7. 进程池和线程池 concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
  8. 解析Node.js通过axios实现网络请求
  9. mysql 字符串函数
  10. intellij idea+easychm生成帮助文档