原文地址: http://www.cxy.me/doc/4584.htm
CodeDom是.net提供源代码生成器,他可以通过CodeDomProvider来生成源代码,因为CodeDomProvider是CSharpCodeProvider、VBCodeProvider的抽象基类,表示CodeDomProvider是跨语言的源代码生成器,只需要提供相应的CodeProvider就可以生成相应的语言的源代码。
CodeDom在System命名空间下,CSharpCodeProvider在Microsoft.CSharp命名空间下。VBCodeProvider在Microsoft.VisualBasic命名空间下。CodeDom里的类及相关的用法
1.CodeCompileUnit类
这个类是用来为CodeDom程序容器提供容器,可以通过他的Namespaces集合的Add方法加入一个命名空间
如:
用来表明一个命名空间对象,这个命名空间里可以引入其他命名空间,也可以加入类
如:加入一个类到命名空间里
引入一个命名空间
用来表示类、结构、接口或枚举的类型声明。
声明一个类如
如:
CodeCompileUnit unit = new CodeCompileUnit();
CodeNamespace theNamespace = new CodeNamespace(\"MyNamespace\");
unit.Namespaces.Add(theNamespace);
2.CodeNamespace类
用来表明一个命名空间对象,这个命名空间里可以引入其他命名空间,也可以加入类
如:加入一个类到命名空间里
CodeTypeDeclaration ETClass = new CodeTypeDeclaration(\"ETMyClasses\");
theNamespace.Types.Add(ETClass);
引入一个命名空间
CodeNamespaceImport SystemImport = new CodeNamespaceImport(\"System\");
theNamespace.Imports.Add(SystemImport);
3.CodeTypeDeclaration类
用来表示类、结构、接口或枚举的类型声明。
声明一个类如
CodeTypeDeclaration ETClass = new CodeTypeDeclaration(\"ETMyClasses\");
BaseTypes 基类或接口的集合,通过Add方法添加
TypeAttributes 类或接口的修饰符,如public、sealed,直接赋值,可以通过|进行或运算
Members 类成员的集合,通过Add方法可以添加字段、属性、方法等
IsEnum 设置或获取这个类型是否是枚举
IsClass 设置或获取这个类型是否是类
IsInterface 设置或获取这个类型是否是接口
IsPartial 设置或获取这个类是完整声明还是部分声明
IsStruct 设置或获取这个类型是否是结构
Name 设置或获取这个类型的名称
CodeTypeDeclaration的构造函数的参数可以是这个类的名称,也可以没有参数,在实例化之后直接指定他的名称。给一个类型添加基类时可以直接使用类名称,如:
TypeAttributes 类或接口的修饰符,如public、sealed,直接赋值,可以通过|进行或运算
Members 类成员的集合,通过Add方法可以添加字段、属性、方法等
IsEnum 设置或获取这个类型是否是枚举
IsClass 设置或获取这个类型是否是类
IsInterface 设置或获取这个类型是否是接口
IsPartial 设置或获取这个类是完整声明还是部分声明
IsStruct 设置或获取这个类型是否是结构
Name 设置或获取这个类型的名称
CodeTypeDeclaration的构造函数的参数可以是这个类的名称,也可以没有参数,在实例化之后直接指定他的名称。给一个类型添加基类时可以直接使用类名称,如:
ETClass.BaseTypes.Add(\"EntitisGateway\");
也可以添加CodeTypeReference的实例,如:
ETClass.BaseTypes.Add(new CodeTypeReference(typeof(System.Console)));
4.CodeNamespaceImport类
用来表示映入的Namespace对象,构造函数参数为无参或命名空间名,通过CodeTypeDeclaration的Imports集合的Add方法添加命名空间的
5.CodeMemberField类
用来表明类里面的字段对象,构造函数的参数为类型和名称如
CodeMemberField tableNameFiled = new CodeMemberField(typeof(string), \"C_STR_TABLENAME\");
这是表明定义了一个类型为string,名字为C_STR_TABLENAME的类成员字段
Attributes 设置类的描述符号如Const、public,可以用|进行或运算
InitExpression 初始化字段的表达式
注意对InitExpression赋值需要使用CodePrimitiveExpression类,他是用来表示使用指定对象来初始化,如:
tableFieldField.InitExpression = new CodePrimitiveExpression(field.FieldName);
6.CodeMemberProperty类
用来表明类里面的属性对象构造函数为无参构造函数Name 属性的名称Type 属性的类型,使用CodeTypeReference来进行赋值
Attributes 表明属性的修饰符public、override
HasGet 获取或设置该属性是否有get方法访问器
HasSet 获取或设置该属性是否有set方法访问器
GetStatements 表明get方法访问器的语句集合,通过Add方法添加语句
SetStatements 表明set方法访问器的语句集合,通过Add方法添加语句
给属性的get或set方法访问器添加语句如下例:
//定义一个表达式,他用来表示一个局部变量的引用
CodeVariableReferenceExpression fieldExpression = new CodeVariableReferenceExpression(\"C_STR_TABLENAME\");
//定一个返回语句,用来表示get的返回语句,他返回的变量是fieldExpression
CodeMethodReturnStatement propertyReturn = new CodeMethodReturnStatement(fieldExpression);
//将返回语句添加到Get的语句集合中
tableNameProperty.GetStatements.Add(propertyReturn);
7.CodeMemberMethod类
表示类里的一个成员方法,只有无参构造函数Name 表示方法名
Attributes 方法的描述符,可以通过|进行或运算
Parameters 方法的参数集合,通过Add方法添加参数
Statements 方法中的语句集合,通过Add方法添加
//构造一个方法对象
CodeMemberMethod LoadDataByProcessIDMethod = new CodeMemberMethod();
//指定方法名
LoadDataByProcessIDMethod.Name = \"LoadDataByProcessID\";
//指定方法的修饰符
LoadDataByProcessIDMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
//给方法添加一个类型为int,名称为ProcessID的参数,通过CodeParameterDeclarationExpression来表示一个参数
LoadDataByProcessIDMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), \"ProcessID\"));
LoadDataByProcessIDMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbTransaction), \"objTrans\"));
//给定方法的引用,表明要调用一个方法
CodeMethodReferenceExpression LoadDataExpression = new CodeMethodReferenceExpression();
//调用的方法名
LoadDataExpression.MethodName = \"LoadDataBy\";
//定义方法调用的表达式,并将调用方法的引用传入,使用CodeVariableReferenceExpression来表示调用方法时传入的引用
CodeMethodInvokeExpression LoadDataInvoke = new CodeMethodInvokeExpression(LoadDataExpression, new CodeVariableReferenceExpression(\"C_STR_PROCESSID + \"=\" + ProcessID\"), new CodeVariableReferenceExpression(\"objTrans\"));
//将方法调用的表达式添加到方法额语句集合中
LoadDataByProcessIDMethod.Statements.Add(LoadDataInvoke);
//将方法添加到类中
ETClass.Members.Add(LoadDataByProcessIDMethod);
CodeParameterDeclarationExpression是用指定的类型和参数名来初始化一个方法的参数。CodeVariableReferenceExpression用来表明一个局部变量名的引用
8.CodeConstructor类
用来表示一个类的构造函数,BaseConstructorArgs是用来表示基类的构造函数的参数集合,通过Add方法添加,如果基类的构造函数没有参数可以添加一个CodeVariableReferenceExpression对象,不用任何便两名初始化他,如:
wfPropertyConstructor1.BaseConstructorArgs.Add(new CodeVariableReferenceExpression());
9.CodeTypeOfExpression类
用来表示typeof表达式
10.CodeThisReferenceExpression类
用来表示this的表达式
11.CodeBaseReferenceExpression类
用来表示base的表达式
CodeThisReferenceExpression和CodeBaseReferenceExpression可以和其他表达式组合在一起形成一个表达式如:
CodeMethodInvokeExpression InitByEntityIDInvoke = new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), \"ConstructByID\", new CodeVariableReferenceExpression(m_Model.EntitiesTable + \"ID\"), new CodeVariableReferenceExpression(\"objTrans\"));
上面这段代码就是初始化一个方法调用的表达式,使用CodeBaseReferenceExpression和ConstructByID方法,加上两个局部变量的作为一个调用方法的参数组合成一个函数调用的表达式
12.CodeSnippetStatement类
表明一条语句,由于CodeDom为了兼容VB的语法,有些语法不支持,没有类可以调用,实在没有办法了可以通过CodeSnippetStatement来写一条语句,但这个语句不能保证在VB.Net里被通过
CodeSnippetStatement ExceptionStatement = new CodeSnippetStatement(\"throw new Exception(\"Property not Implementation\")\");
上面这条抛出异常的语句是直接写的代码,当然CodeDom存在异常语句的对象,我这里偷了一下懒
13.IndentedTextWriter类
表明生成出来代码语句的缩进
IndentedTextWriter tw = new IndentedTextWriter(new StreamWriter(sourceCodePaht + \"\\\" + m_Model.EntitiesAssembly + \".cs\", false), \" \");
false后面的双引号之间的四个空格表明代码的缩进值为四个空格
那么我们剩下的就是生成代码了
IndentedTextWriter tw = new IndentedTextWriter(new StreamWriter(sourceCodePaht + \"\\\" + m_Model.EntitiesAssembly + \".cs\", false), \" \");
CodeDomProvider provide = new CSharpCodeProvider();
provide.GenerateCodeFromCompileUnit(unit, tw, new CodeGeneratorOptions());
tw.Close();
provider提供几种生成,GenerateCodeFromCompileUnit可以生成的直接生成一个代码容器里的所有代码,如果我们只写了一个类,可以通过GenerateCodeFromType方法生成,也可以生成一个命名空间里的代码,甚至可以只生成一条语句或一个表达式GenerateCodeFromNamespace、GenerateCodeFromStatement
剩下的就是要编译源代码了
CompilerParameters compilerParams = new CompilerParameters();
//指定编译选项,是类库
compilerParams.CompilerOptions = \"/target:library /optimize\";
//不生成执行文件
compilerParams.GenerateExecutable = false;
//在内存中生成输出
compilerParams.GenerateInMemory = true;
//不包含调试信息
compilerParams.IncludeDebugInformation = false;
//添加编译时使用的dll,如果不是系统自带的dll,必须指定完整的路径
compilerParams.ReferencedAssemblies.Add(\"mscorlib.dll\");
compilerParams.ReferencedAssemblies.Add(\"System.dll\");
compilerParams.ReferencedAssemblies.Add(\"System.Data.dll\");
compilerParams.ReferencedAssemblies.Add(AppDomain.CurrentDomain.SetupInformation.ApplicationBase + \"DawnPro.WebArchitechture.DAL.dll\");
compilerParams.ReferencedAssemblies.Add(AppDomain.CurrentDomain.SetupInformation.ApplicationBase + \"DawnPro.WorkflowInterface.dll\");
//指定编译后的文件的输出路径
compilerParams.OutputAssembly = outputAsseblyPath + \"\\bin\\\" + m_Model.EntitiesAssembly + \".dll\";//最后使用Provider提供的Compil//eAssemblyFromDom方法进行编译,provider提供了三种方法,一种是从Dom,还有一种从文件,一种从字符串中编译CompilerResults result = provide.CompileAssemblyFromDom(compilerParams, unit);//CompilerResults是这三种方法的返回值,可以通过CompilerResults的Errors集合来查看编译的错误。
14.CodeDom的例子
最后,是一个CodeDom的例子 public static void GenerateToClass()
{
//声明代码的部分
CodeCompileUnit compunit = new CodeCompileUnit();//这个类是用来为CodeDom程序容器提供容器,可以通过他的Namespaces集合的Add方法加入一个命名空间
CodeNamespace sample = new CodeNamespace();//用来表明一个命名空间对象,这个命名空间里可以引入其他命名空间,也可以加入类
//引用命名空间
sample.Imports.Add(new CodeNamespaceImport("System"));//导入System命名空间
sample.Imports.Add(new CodeNamespaceImport("System.Linq"));//导入System.Linq命名空间
sample.Name = "DynamicClass";
compunit.Namespaces.Add(sample);
//在命名空间下添加一个类
CodeTypeDeclaration wrapProxyClass = new CodeTypeDeclaration("ClassName1");
//wrapProxyClass.BaseTypes.Add(baseType);// 如果需要的话 在这里声明继承关系 (基类 , 接口)
wrapProxyClass.CustomAttributes.Add(new CodeAttributeDeclaration("Serializable"));//添加一个Attribute到class上
sample.Types.Add(wrapProxyClass);//把这个类添加到命名空间 ,待会儿才会编译这个类
//为这个类添加一个无参构造函数 其实不添加也没事的, 只是做个demo而已
CodeConstructor constructor = new CodeConstructor();
constructor.Attributes = MemberAttributes.Public;
wrapProxyClass.Members.Add(constructor);
//为这个类添加一个方法 public override int 方法名(string str);
System.CodeDom.CodeMemberMethod method = new CodeMemberMethod();
method.Name = "Method";
method.Attributes = MemberAttributes.Static | MemberAttributes.Public;//声明方法是公开 并且override的
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "str")); //这个方法添加一个输入参数
method.ReturnType = new CodeTypeReference(typeof(int));//声明返回值的类型
method.Statements.Add(new CodeSnippetStatement(" return 1; ")); //方法体里面很简单 直接返回 一个1;
wrapProxyClass.Members.Add(method);
CSharpCodeProvider cprovider = new CSharpCodeProvider();//提供对 C# 代码生成器和代码编译器的实例的访问
ICodeGenerator gen = cprovider.CreateGenerator();//C# 代码生成器的实例。
//CS代码文件的字符串
StringBuilder fileContent = new StringBuilder();
using (StringWriter sw = new StringWriter(fileContent))
{
//具体使用方法及参数设置,可以到网上查找CodeGeneratorOptions 的用法,
CodeGeneratorOptions cgo = new CodeGeneratorOptions();//允许我们指定各种供代码生成器使用的格式化选项
cgo.BracingStyle = "C"; // 指定格式:花括号的位置
//cgo.IndentString = "\t"; // 指定格式:代码块的缩进方式
cgo.BlankLinesBetweenMembers = true;//默认为:true,获取或设置一个值,该值指示是否在成员之间插入空行,
gen.GenerateCodeFromCompileUnit(compunit, sw, new CodeGeneratorOptions());//想把生成的代码保存为cs文件
}
ICodeCompiler compiler = cprovider.CreateCompiler();//C# 代码编译器的实例。
//编译参数
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");//刚才引用了命名空间 这里是添加引用
cp.ReferencedAssemblies.Add("System.Core.dll");//刚才引用了命名空间 这里是添加引用
cp.OutputAssembly = @"E:\DynamicClass.dll";
cp.GenerateInMemory = false; //是否只在内存中生成
cp.IncludeDebugInformation = true;//包含调试符号 pdb文件
cp.GenerateExecutable = false;//生成dll,不是exe
cp.WarningLevel = 4;
cp.TreatWarningsAsErrors = false;
string filePath = @"E:\DynamicClass.cs";
File.WriteAllText(filePath, fileContent.ToString());
CompilerResults cr = compiler.CompileAssemblyFromFile(cp, filePath); //保存文件再进行编译 待会儿调试就比较方便了 ,可以直接断点到刚才生成的文件里面
// CompilerResults cr = compiler.CompileAssemblyFromDom(cp, compunit); //这样的生成 不用写文件 ,就是调试麻烦
String outputMessage = "";
foreach (var item in cr.Output)
{
outputMessage += item + Environment.NewLine;//调试的最终输出信息
}
if (cr.Errors.Count > 0)//有编译错误就抛出异常
{
throw new Exception("error:" + Environment.NewLine + outputMessage);
}
}