2011-04-06 00:43:00 jackiej 阅读数 2250

 

(转自:http://bxl766.blog.163.com/blog/static/6109950520100311235553/)

2009-03-09 08:48:00 keke1314520 阅读数 234

本文为原创,如需转载,请注明作者和出处,谢谢!

CodeDOM是.net framework的一项重要的源代码生成技术。本文详细讨论了CodeDOM的原理以及如何利用CodeDOM技术实现一个与语言无关的Code Wizard。并给出了一个用C#语言实现的例子。

一、什么是CodeDom?

现在的程序规模越来越 大,虽然在计算发展的几十年间,产生了许多快捷、高效的编程语言和开发工具,如C#、Visual Studio、java等。也产生了许多用以辅助软件设计、开发的思想和方法,如UML、OOP、Agile等。尽管利用这些技术和方法可以大大提高程序 编写的效率,但是仍可能有重复的编码工作。因此,现在出现了许多可以自动产生源代码或者目标文件的软件,即Code Wizards。
一般这些Code Wizards在生成源代码时都是通过设置模板文件,然后根据这些模板文件生成源代码。有很多Code Wizards只能生成固定的语言(如java、C#等)。虽然有一些Code Wizards可 以生成多种语言,但也只是固定的几种。而且生成源代码部分都是显示地固定在程序中。这样非常不易扩展。如CodeSmith系统,这是一个非常不错的 Code Wizard。它使用一个扩展名为cst的文件来设置模板。这个模板文件的格式类似于Asp.net。如果想生成C#源代码,必须要在其中显示地标明,并 且模板的固定部分要使用C#语言编写。如果这样的话,同样功能要生成不同语言的代码,如C#和VB.net。就要编写两个模板文件。这是非常不方便的。
从以上的描述来看, Code Wizard所 面临的一个重要问题就是如何使用一个模版文件来生成不同语言的源代码。幸好Microsoft提供了一种解决方案,这就是CodeDOM技术。 CodeDOM的全称是代码文档对象模型(Code Document Object Model)。整个CodeDOM就是一张对象图(object graph)。它用这张图中的所有对象描述了面向对象语言中的几乎所有的语法现象,如类、接口、方法、属性等。
CodeDOM通过对象模型对语言进行了抽象,然后利用具体语言所提供的生成源代码的机制来生成源代码,并可调用相应的编译器将源码生成*.dll或*.exe。从而可以达到与语言无关的目的。图1描述了使用CodeDOM生成和编译源代码的过程。



图1CodeDom生成和编译源代码的过程

从上图可以看出,CodeWizard只使用CodeDOM对语言进行抽象,然后通过CodeDomProvider生成源代码。最后通过编译器生成中间语言。下面将详细讨论如何利用CodeDOM来实现CodeWizard。

二、实现CodeWizard

下面要实现的这个 CodeWizard非常简单。其功能主要是将一个数据表映射成一个类。这个类提供了Add和Save方法以及和数据表的每个字段相对应的属性。使用这个 类可以向数据表添加记录。为了便于描述,将这个数据表保存成xml文件格式。每条记录为一个item结点,每一个字段为这个结点的一个属性。表名为这个 xml文件的根结点名称。这个xml文件的格式如下所示:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><MyTable>
<itemid="01"name="Bill"/>
<itemid="02"name="Mike"/>
</MyTable>

这个CodeWizard通过一个模板文件来定义数据表的结构。模板文件的格式如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><MyTable>
<idtype="System.Int32"/>
<nametype="System.String"/>
</MyTable>

其中type为字段的类 型,它的值是在.net framework中的System中定义的简单类型,如System.Int32、System.String、System.Char等。下面就详细 讨论如何利用这个模板文件和CodeDOM技术来生成C#和VB.net的源代码。

三、CodeDOM的结构

CodeDOM由两部分组成:

1. 用于描述抽象代码结构的一组类。其中CodeCompileUnit类是这些类的根。代表一个源码文件(如C#的*.cs和VB.net的*.vb)。在使用CodeDOM时,必须先建立一个CodeCompileUnit类的对象,然后在这个对象中加入必要的namespace、class等面向对象元素。

用于生成和编译源代码的类。这个类必须从CodeDomProvider类继承。每种.net framework

所支持的语言都有自己的CodeDomProvider类。如在C#中的CodeDomProvider类叫CSharpCodeProvider,而在VB.net中叫VBCodeProvider

四、数据表类的定义

要用CodeDOM定义一个类需要三步:

1. 建立一个CodeCompileUnit对象。这个类相当于一个源码文件。

2. 建立一个CodeNamespace对象。理论上在.net framework上运行的程序语言,如C#、VB.net

等,可以没有namespace。但在CodeDOM中必须使用这个类,如果不想要namespace,可以将namespace的名字设为 null或空串。

建立一个CodeTypeDeclaration对象。这个类可以建立Class和Interface两种Type。在

这个例子中只建立Class。如果想建立Interface,只需将IsInterface属性设为true即可。

主要的实现代码如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->privateCodeCompileUnitm_CodeCompileUnit;
privateCodeNamespacem_CodeNameSpace;
privateCodeTypeDeclarationm_Class;
privatevoidInitCodeDom()
{
m_CodeCompileUnit
=newCodeCompileUnit();
m_CodeNameSpace
=newCodeNamespace("xml.tables");
m_CodeCompileUnit.Namespaces.Add(m_CodeNameSpace);
m_Class
=newCodeTypeDeclaration(m_ClassName);
m_CodeNameSpace.Types.Add(m_Class);
}

其 中namespace的名子是“xml.tables”。在建立完namespace后,将其加入到m_CodeCompileUnit的 Namespaces集合中。m_ClassName是一个String变量,它的值就是数据表的表名。最后将所建立的类加入到namespace的 Types集合中。在产生完类后。需要在这个类中加入四部分内容,它们分别是:全局变量、属性、构造函数和方法(Add和Save方法)。下面就分别讨论 它们的实现过程。

五、全局变量的生成

这个数据表类中有四种全局变量:用于操作xml文件的类型为XmlDocument的变量、用于保存数据表文件名的变量、用于确定是否为加入状态的Boolean型变量、以及用于保存每个字段值的变量组。具体实现代码如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->privatevoidGenerateFields()
{
//产生"privateXmlDocumentm_xml=newXmlDocument();"
CodeMemberFieldxml=newCodeMemberField("System.Xml.XmlDocument","m_xml");
CodeObjectCreateExpressioncreatexml
=newCodeObjectCreateExpression("System.Xml.XmlDocument");
xml.InitExpression
=createxml;
m_Class.Members.Add(xml);
//产生"privateStringm_XmlFile;"
CodeMemberFieldxmlfile=newCodeMemberField("System.String","m_XmlFile");
m_Class.Members.Add(xmlfile);
//根据模板文件产生保存字段值的变量
Stringfieldname="",fieldtype="";
foreach(XmlNodexninm_Xml.DocumentElement.ChildNodes)
{
fieldname
="m_"+xn.Name;
fieldtype
=xn.Attributes["type"].Value;
CodeMemberFieldfield
=newCodeMemberField(fieldtype,fieldname);
m_Class.Members.Add(field);
}
//产生"privateboolm_AddFlag;"
CodeMemberFieldaddflag=newCodeMemberField("System.Boolean","m_AddFlag");
m_Class.Members.Add(addflag);
}

在以上代码中每段程序上方的注释是它们所生成的C#源代码。在输入这段代码之前,需要引入两个namespace。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->usingSystem.CodeDom;
usingSystem.CodeDom.Compiler;

五、属性的生成

在数据表类中每个属性代表数据表的一个字段,名子就是字段名。这些属性和保存字段的全局变量一一对应。下面是具体的实现代码:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->privatevoidGenerateProperties()
{
Stringfieldname
="",fieldtype="";
foreach(XmlNodexninm_Xml.DocumentElement.ChildNodes)
{
fieldname
=xn.Name;
fieldtype
=xn.Attributes["type"].Value;
CodeMemberPropertyproperty
=newCodeMemberProperty();
property.Attributes
=MemberAttributes.Public|MemberAttributes.Final;
property.Name
=fieldname;
property.Type
=newCodeTypeReference(fieldtype);
property.HasGet
=true;
property.HasSet
=true;
CodeVariableReferenceExpressionfield
=newCodeVariableReferenceExpression("m_"+fieldname);
//产生returnm_property
CodeMethodReturnStatementpropertyReturn=newCodeMethodReturnStatement(field);
property.GetStatements.Add(propertyReturn);
//产生m_property=value;
CodeAssignStatementpropertyAssignment=newCodeAssignStatement(field,
new
CodePropertySetValueReferenceExpression());
property.SetStatements.Add(propertyAssignment);
m_Class.Members.Add(property);
}
}

这些生成的属性是可读写的。这就需要将HasGet和HasSet两个属性设为true,然后分别将get和set方法中的语句分别加到GetStatements和SetStatements中。

六、构造函数的生成

构造函数的主要工作是打开数据表。如果数据表不存在,就创建这个数据表文件。在编写代码之前,需要先定义三个全局变量。因为这三个全局变量在程序中会多次用到。它们的类型都是CodeVariableReferenceExpression这个类型变量其实在生成源码中的作用就是对某一个变量的引用。具体的实现代码如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->privateCodeVariableReferenceExpressionm_XmlFileExpression;
privateCodeVariableReferenceExpressionm_XmlExpression;
privateCodeVariableReferenceExpressionm_AddFlagExpression;
privatevoidInitVariants()
{
m_XmlFileExpression
=newCodeVariableReferenceExpression("m_XmlFile");
m_XmlExpression
=newCodeVariableReferenceExpression("m_xml");
m_AddFlagExpression
=newCodeVariableReferenceExpression("m_AddFlag");
}

下面是生成构造函数的源代码:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->privatevoidGenerateConstructor()
{
//定义构造函数
CodeConstructorconstructor=newCodeConstructor();
constructor.Parameters.Add(
newCodeParameterDeclarationExpression("System.String","xmlFile"));
constructor.Attributes
=MemberAttributes.Public;
//产生"m_XmlFile=xmlFile;"
CodeAssignStatementassignXmlFile=newCodeAssignStatement(m_XmlFileExpression,
new
CodeVariableReferenceExpression("xmlFile"));
//产生"m_xml.LoadXml("…");"
CodeMethodInvokeExpressioninvokeLoadXml=newCodeMethodInvokeExpression(m_XmlExpression,"LoadXml",
newCodePrimitiveExpression("<?xmlversion=\"1.0\"encoding=\"gb2312\"?><"+m_Xml.DocumentElement.Name
+
"></"+m_Xml.DocumentElement.Name+">"));
//产生"m_xml.Save(m_XmlFile);"
CodeMethodInvokeExpressioninvokeSave=newCodeMethodInvokeExpression(m_XmlExpression,"Save",
m_XmlFileExpression);
CodeStatementCollectionstatements
=newCodeStatementCollection();
statements.Add(invokeLoadXml);
statements.Add(invokeSave);
//产生if语句:"if(System.IO.File.Exists(m_XmlFile))else"
CodeConditionStatementifStatement=newCodeConditionStatement(newCodeMethodInvokeExpression(
new
CodeVariableReferenceExpression("System.IO.File"),"Exists",m_XmlFileExpression),newCodeStatement[]{},
newCodeStatement[]{statements[0],statements[1]});
//产生"m_xml.Load(m_XmlFile);"
CodeMethodInvokeExpressioninvokeLoad=newCodeMethodInvokeExpression(m_XmlExpression,"Load",
m_XmlFileExpression);
//产生"m_AddFlag=false;"
CodeAssignStatementassignAddFalse=newCodeAssignStatement(m_AddFlagExpression,
newCodePrimitiveExpression(false));
constructor.Statements.Add(assignXmlFile);
constructor.Statements.Add(ifStatement);
constructor.Statements.Add(invokeLoad);
constructor.Statements.Add(assignAddFalse);
m_Class.Members.Add(constructor);
}


七、Add和Save方法生成

Add方法只有一条语句,功能是将m_AddFlag设为true,以使数据表类处于加入状态。Save方法比较复杂。它的功能是当m_AddFlag为true时在数据表文件的最后加入一条记录,并保存。具体实现代码如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->privatevoidGenerateMethods()
{
CodeTypeReferencevoidReference
=newCodeTypeReference("System.void");
//产生Add方法
CodeMemberMethodadd=newCodeMemberMethod();
add.ReturnType
=voidReference;
add.Name
="add";
add.Attributes
=MemberAttributes.Public|MemberAttributes.Final;
CodeAssignStatementassignAddTrue
=newCodeAssignStatement(m_AddFlagExpression,
newCodePrimitiveExpression(true));
add.Statements.Add(assignAddTrue);
m_Class.Members.Add(add);
//产生Save方法
CodeMemberMethodsave=newCodeMemberMethod();
save.ReturnType
=voidReference;
save.Name
="save";
save.Attributes
=MemberAttributes.Public|MemberAttributes.Final;
System.Collections.Generic.List
<CodeStatement>ifStatements=
newSystem.Collections.Generic.List<CodeStatement>();
//产生"XmlNodexn=m_xml.CreateNode(XmlNodeType.Element,"item","");"
CodeVariableDeclarationStatementxmlNode=newCodeVariableDeclarationStatement("System.Xml.XmlNode","xn");
CodeMethodInvokeExpressioncreateNode
=newCodeMethodInvokeExpression(m_XmlExpression,"CreateNode",
newCodeExpression[]{newCodeVariableReferenceExpression("System.Xml.XmlNodeType.Element"),
newCodePrimitiveExpression("item"),
new
CodePrimitiveExpression("")});
xmlNode.InitExpression
=createNode;
ifStatements.Add(xmlNode);
//产生"XmlAttributexa=null;"
CodeVariableDeclarationStatementxmlAttr=newCodeVariableDeclarationStatement("System.Xml.XmlAttribute","xa");
xmlAttr.InitExpression
=newCodePrimitiveExpression(null);
ifStatements.Add(xmlAttr);
//产生字段属性
CodeStatementCollectionstatements=newCodeStatementCollection();
foreach(XmlNodexninm_Xml.DocumentElement.ChildNodes)
{
CodeMethodInvokeExpressioncreateAttribute
=newCodeMethodInvokeExpression(m_XmlExpression,
"CreateAttribute",newCodePrimitiveExpression(xn.Name));
CodeAssignStatementassignxa
=newCodeAssignStatement(
new
CodeVariableReferenceExpression("xa"),createAttribute);
CodeMethodInvokeExpressioninvokeToString
=newCodeMethodInvokeExpression(
new
CodeVariableReferenceExpression("m_"+xn.Name), "ToString");
CodeAssignStatementassignValue
=newCodeAssignStatement(
newCodeVariableReferenceExpression("xa.Value"),invokeToString);
CodeMethodInvokeExpressioninvokeAppend
=newCodeMethodInvokeExpression(
new
CodeVariableReferenceExpression("xn.Attributes"),
"Append",new CodeVariableReferenceExpression("xa"));
statements.Add(invokeAppend);
ifStatements.Add(assignxa);
ifStatements.Add(assignValue);
ifStatements.Add(statements[
0]);
}
//产生"m_xml.DocumentElement.AppendChild(xn);"
CodeMethodInvokeExpressioninvokeAppendChild=newCodeMethodInvokeExpression(new
CodeVariableReferenceExpression(
"m_xml.DocumentElement"),"AppendChild",
new
CodeVariableReferenceExpression("xn"));
statements.Clear();
statements.Add(invokeAppendChild);
ifStatements.Add(statements[
0]);
//产生"m_xml.Save(m_XmlFile);"
CodeMethodInvokeExpressioninvokeSave=newCodeMethodInvokeExpression(m_XmlExpression,
"Save",m_XmlFileExpression);
statements.Clear();
statements.Add(invokeSave);
ifStatements.Add(statements[
0]);
//产生"m_AddFlag=false;"
CodeAssignStatementassignAddFalse=newCodeAssignStatement(m_AddFlagExpression,
newCodePrimitiveExpression(false));
ifStatements.Add(assignAddFalse);
//产生if语句:"if(m_AddFlag)"
CodeConditionStatementifStatement=newCodeConditionStatement(m_AddFlagExpression,
ifStatements.ToArray());
save.Statements.Add(ifStatement);
m_Class.Members.Add(save);
}

八、生成源代码

生成具体语言的源代码需要一个从CodeDomProvider继承的类。对于C#而言是CSharpCodeProvider类。实现代码如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->usingMicrosoft.CSharp;
publicvoidSaveCSharp(Stringfilename)
{
IndentedTextWritertw
=newIndentedTextWriter(newStreamWriter(filename,false),"");
CodeDomProviderprovide
=newCSharpCodeProvider();
provide.GenerateCodeFromCompileUnit(m_CodeCompileUnit,tw,
newCodeGeneratorOptions());
tw.Close();
}

在使用CSharpCodeProvider类时需要用到m_CodeCompileUnit这个全局变量。这样可产生一个*.cs文件。以上代码中的IndentedTextWriter类是建立一个文件的Writer,用于向这个文件中输出源代码。但和其它的Writer不同的是它的输出是缩进的(以四个空格进行缩进)。如是想生成VB.net的代码,只需将CSharpCodeProvider改为VBCodeProvider即可。

九、编译源代码

到 现在为止,这个数据表类的源代码已经全部生成了。你可以将这个源文件直接加入到自己的工程中。或者直接将其编译成*.dll文件,然后在程序中调用。如果 想编译,可以直接调用指定语言的编译器(如C#中的csc.exe)。但这样不是太方便。在CodeDOM中提供了一种机制,可以在程序中通过 CodeDomProvider直接调用指定语言的编译器。下面是编译C#源程序的一个例子。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicvoidCompileCSharp(Stringsourcefile,StringtargetFile)
{
CompilerParameterscp
=newCompilerParameters(newString[]{"System.Xml.dll"},targetFile,false);
CodeDomProviderprovider
=newCSharpCodeProvider();
cp.GenerateExecutable
=false;
//调用编译器
CompilerResultscr=provider.CompileAssemblyFromFile(cp,sourcefile);
if(cr.Errors.Count>0)
{
//显示编译错误
foreach(CompilerErrorceincr.Errors)
System.Windows.Forms.MessageBox.Show(ce.ToString());
}
}

对于以上代码有两点说明:

  1. 使用CodeDomProvider调用编译器时也需要传递相应的参数,如在本例中将System.Xml.dll

作为一个参数,表示目标文件需要调用这个dll中的资源。

  1. 在调用编译器后,如果出现错误,可使用cr.Errors获得错误信息。

十、结束语

我花了一个晚上的时间实现了这个简单的例子,并用C#2.0调试通过,只是为了抛砖引玉。自动生成源代码有很多的方法,但使用CodeDom生成源代码会有更大的灵活性,主要表现在以下三个方面:

1. 语言无关。即只要是.net framework所支持的语言,并且这种语言提供了CodeDomProvider。

就可以生成这种语言的源代码。

2. 如果所生成的语言是测试版或要将这种语言升级到下一个版本,也可以考虑使用CodeDOM。

因为当这种语言的语法有所变化时,CodeDomProvider也会随之升级。因此,使用CodeDOM的Code Wizards也会随着CodeDOM而升级,这样就不必修改Code Wizards的源代码了。

3. 如果所生成的一种语言是你所不熟悉的,如果不使用CodeDOM,必须要熟悉这种语言的语

法,才能生成它的源代码。而使用CodeDOM却可以避免这一点。因为CodeDOM是使用抽象的object graph来描述语言的。而语言的具体语法是由CodeDomProvider所决定的。

其实CodeDOM不仅可以用在Code Wizards上,也可以用在许多其它地方,如可以生成Web

Services的客户端代理(Client Proxies),或根据UML图生成类的构架代码。总之,使用CodeDom可以大大降低和语言的偶合度,并且很容易维护和升级系统。

2012-09-27 10:33:00 weixin_30322405 阅读数 19

 

  前段时间有个项目,因为其中需要使用脚本做模板数据的计算,因此让我对脚本引擎产生了兴趣,于是上网到处寻找资料,于是找到了《龙书》、CodeDom。于是又找了半天,找不到开源的脚本引擎源码,脑袋一热,干脆自己写一个,于是就有了这个用纯c#(非CodeDom)编写的脚本引擎。编写过程中收获良多,目前还不是很完善,效率也不高,仅供学习用,后面会上传源代码。

  下面说明一下:语法参考Javascript,基本的javascript语法都已实现,但是有些内建对象没实现,例如 Date,数据类型支持 数值,字符串(考虑到在代码中执行代码,所以字符串的定义用 '字符串' 而非 "字符串"),对象,布尔值,数组。语法方面基本参考Javascript就行了。在编写这个东西的时候顺带学习了下CodeDom(感叹枉费我弄C#也4年多了,现在才知道这东西,看来水还是太深了),在考虑语法分析的时候就参考了CodeDom的对象结构,如下:

  命名空间定义:

    ScriptEngin.Code :词法分析器

    ScriptEngin.CodeDom:编译后的解释模型,就是上图的结构(都说了是参考CodeDom)

    ScriptEngin.Exceptions:自定义的异常类

      ScriptEngin.Parser:语法分析器

    ScriptEngin.RunTime:运行时解释执行器

    ScriptEngin.RunTimeType:处理c#对象和脚本对象的映射(还未完善)

    下面贴一段调用代码:

    

 1 string code1 = @"
 2 
 3 var str='字符串申明';
 4 var obj={name:'对象申明',num:100,fun:function(a,b){return a+b;}};
 5 var arr=['这个是数组','123'];
 6 var arr2=[['多维数组','111'],['222','223']];
 7 
 8 function testClass()
 9 {
10     this.name='成员属性';
11     this.MemFun=function(){
12         return '成员函数';
13     }
14 }
15 var newObj=new testClass();
16 var nameMem=newObj.name;
17 
18 
19 function testFun(a,v)
20 {
21    count=0;
22    for(i=0;i<v;i++)
23    {
24        count+=a;
25    }
26    return count;
27 }
28 
29 
30 ";
31             Script scr = new Script();
32             //设置传入脚本的参数,这里传入的参数在脚本中可做变量用
33             scr.Paramers.Set("testbool", true);
34             scr.Paramers.Set("testString", "abc");
35 
36             //编译并运行全局代码
37             scr.Run(code1);
38 
39             //获取运行时变量,PS:暂时只能返回数值、字符串、Boolean值
40             object varValue = scr.Paramers.Get("str");
41 
42             //调用指定函数,能返回的数据同上
43             object value= scr.Call("testFun",300,200);

 

   目前未做大量测试,可能会存在些许Bug,如果大家发现了请告诉我,谢谢。

  (第一次写博,大家见笑了)

   源码在此

 


  呵呵,感谢大家的支持!

  关于有人问这个东西在实际项目中的用处这个问题,这个嘛其实还是很多的,例如在项目中有些地方是需要用户自定义的,比如报表模板,界面模板等等。当用户需要做一些简单计算的时候,脚本引擎的作用就可以体现出来了。总不可能当用户需要在模板中做计算的时候,你来给他修改代码然后重新发布吧(这里我说的不是很清楚)。另外,作为一个程序员,多了解一些编译原理的东西总没坏处的。

  呵呵,如果对脚本引擎和编译原理感兴趣的人多的话,我也许会写一系列的博文,用最浅显的语言给你解释原理,用较简单的方法让你知道如何写一个脚本引擎。其实看了《龙书》快4个多月的时间了,其中的很多东西也还不是很明白,最快乐的就是某天的某个时刻忽然领悟其中某部分知识的时候,那种好心情能让自己高兴一整天,恨不得马上坐下写出代码来验证自己的想法。

  希望和大家一起学习,一起研究,我们是程序员,是计算机的灵魂工程师~~~呵呵

转载于:https://www.cnblogs.com/TianY/archive/2012/09/27/2704267.html

2012-05-24 09:56:00 weixin_30389003 阅读数 13

很早就研究过CodeDom,当时觉得挺有意思的,但是一时也想不到具体应用在哪,于是写了个DEMO就放在那里了。

昨天项目里有个地方要把一大堆值拿来计算公式,每一个值还都是需要单独计算求和,并且最后的公式还不一定,最后还需要输出所有的值,搞的我有点头大,其实也不算太麻烦,但是手工定义一大堆变量再去操作我怎么想怎么不爽,突然想起CodeDom,于是尝试了下, 果然爽多了。

首先构造类,将所有要计算的值都构造成变量,把变量的赋值加减和公式的计算都放在类的一个方法里,构造完类之后调用一下方法,该计算的就都计算完了,最后用反射输出所有值就OK了。

虽然是个简单的应用,不过还是很有感觉,嘿嘿,不写重复的东西就是爽。

private string GetSumScript(Dictionary<string, DeviceInfo> dictAllDevs)
    {
        StringBuilder classSrc = new StringBuilder();
        classSrc.Append(" using  System;" + Environment.NewLine);
        classSrc.Append(" namespace  A { " + Environment.NewLine);
        classSrc.Append("public  class  TempSum{ " + Environment.NewLine);//创建temp类
        List<string> propList = new List<string>();
        string strSum = "public void Run(){";//temp类中的计算方法
        foreach (KeyValuePair<string, DeviceInfo> item in dictAllDevs)
        {
            SumValues(ref classSrc, ref strSum, ref propList, item, 4);
            SumValues(ref classSrc, ref strSum, ref propList, item, 10);
            SumValues(ref classSrc, ref strSum, ref propList, item, 20);
        }
        strSum += "}";
        classSrc.Append(strSum + Environment.NewLine);
        classSrc.Append("}" + Environment.NewLine);
        classSrc.Append("}" + Environment.NewLine);
        object obj = BulidClass(classSrc);//创建类,调用计算方法
        return GetObjFillScript(obj);//输出计算结果
    }
    private void SumValues(ref StringBuilder classSrc, ref string strSum, ref  List<string> propList, KeyValuePair<string, DeviceInfo> item, int valueIndex)
    {
        double value = 0;
        string strProp = "SUMVALUE" + item.Value.Level.ToString() + item.Value.Tagindex + valueIndex.ToString();//类中的变量名
        switch (valueIndex)
        {
            case 4:
                value = item.Value.LoadValue4;
                break;
            case 10:
                value = item.Value.LoadValue10;
                break;
            default:
                value = item.Value.LoadValue20;
                break;
        }
        if (!propList.Contains(strProp))
        {
            classSrc.Append("public double " + strProp + "=" + value + ";" + Environment.NewLine);//如果不存在就声明它
            propList.Add(strProp);
        }
        else
        {
            strSum += GetFormula(strProp, value, item);//存在则使用公式计算
        }
    }
    private object BulidClass(StringBuilder classSrc)
    {
        string source = classSrc.ToString();
        CSharpCodeProvider compiler = new CSharpCodeProvider();
        CompilerParameters paras = new CompilerParameters();
        paras.GenerateExecutable = false;
        paras.GenerateInMemory = true;
        CompilerResults result = compiler.CompileAssemblyFromSource(paras, source);
        Assembly assembly = result.CompiledAssembly;
        object eval = assembly.CreateInstance("A.TempSum");
        MethodInfo method = eval.GetType().GetMethod("Run");//调用计算方法
        object reobj = method.Invoke(eval, null);
        return eval;
    }
    private string GetObjFillScript(object obj)
    {
        string str = "";
        Type type = obj.GetType();
        foreach (FieldInfo fi in type.GetFields())//用反射输出所有值
        {
            str += "content.add('" + fi.Name.ToUpper() + "','" + fi.GetValue(obj) + "',null);";
        }
        return str;
    }

转载于:https://www.cnblogs.com/nightzsze/archive/2012/05/24/2516080.html

一、背景小伙伴们在用

博文 来自: birdfly2015
没有更多推荐了,返回首页