使用CodeDom动态生成类型
.NET 3.5的时候加入了匿名类型这个特性,我们可以直接使用 new {name="abc"} 来直接生成一个对象。这个特性现在应用的地方很多,比如dapper的查询参数都是用匿名对象。
其实匿名对象也不是真的没有名称,编译器在编译后自动会生成一个Type。我们看看IL就知道了。
编译器会自动生成一个叫做<>f__AnonymousType0`1的类型。
动态生成类型
但是有的时候我们可能类型里面的字段都是不确定的,这个时候我们就需要去动态生成一个类型了。
- 动态生成类型第一个想到的就是反射,但是仔细想想反射都是基于现有Type的基础上完成的,咱们现在连Type都没有,所以这条路不通。
- 第二个dynamic,dynamic确实是个好办法,可以动态指定字段的名称,但是有的三方的库不支持比如dapper。
- 最后CodeDom,CodeDom可以在运行时直接生成一个Type。CodeDom生成Type主要分成3步。
比如我们要生成一个Person类:
public class Person
{
public string name;
public ing age;
public Person(string name ,int age)
{
this.name = name;
this.age = age;
}
}
构造类型
private string _ns = "__x";
private string _className;
private Dictionary<Type, string> _fieldsDictionary;
private string _sourceCode;
private CodeCompileUnit _targetUnit;
private CodeTypeDeclaration _targetClass;
public SourceCodeCreater(string className,Dictionary<Type,string> fieldsDictionary )
{
_fieldsDictionary = fieldsDictionary;
_className = className;
_targetUnit = new CodeCompileUnit();
CodeNamespace ns = new CodeNamespace(_ns);
ns.Imports.Add(new CodeNamespaceImport("System"));
_targetClass = new CodeTypeDeclaration(className);
_targetClass.IsClass = true;
_targetClass.TypeAttributes =
TypeAttributes.Public | TypeAttributes.Sealed;
ns.Types.Add(_targetClass);
_targetUnit.Namespaces.Add(ns);
}
public string SourceCode
{
get { return _sourceCode; }
}
public string TypeName
{
get
{
return string.Format("{0}.{1}", _ns, _className);
}
}
private void AddFields()
{
// Declare fields .
foreach (var kv in _fieldsDictionary)
{
CodeMemberField widthValueField = new CodeMemberField();
widthValueField.Attributes = MemberAttributes.Public;
widthValueField.Name = kv.Value;
widthValueField.Type = new CodeTypeReference(kv.Key);
_targetClass.Members.Add(widthValueField);
}
}
private void AddCtor()
{
// Declare constructor
CodeConstructor constructor = new CodeConstructor();
constructor.Attributes =
MemberAttributes.Public | MemberAttributes.Final;
// Add parameters.
foreach (var kv in _fieldsDictionary)
{
constructor.Parameters.Add(new CodeParameterDeclarationExpression(
kv.Key, kv.Value));
}
// Add field initialization logic
foreach (var kv in _fieldsDictionary)
{
CodeFieldReferenceExpression reference =
new CodeFieldReferenceExpression(
new CodeThisReferenceExpression(), kv.Value);
constructor.Statements.Add(new CodeAssignStatement(reference,
new CodeArgumentReferenceExpression(kv.Value)));
}
_targetClass.Members.Add(constructor);
}
我们按照手写类的结构添加字段跟构造函数。
生成CSharp代码
public string Create()
{
AddFields();
AddCtor();
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BracingStyle = "C";
using (StringWriter sourceWriter = new StringWriter())
{
provider.GenerateCodeFromCompileUnit(
_targetUnit, sourceWriter, options);
_sourceCode = sourceWriter.ToString();
}
return _sourceCode;
}
生成CSharp代码
编译
SourceCodeCreater sourceCodeCreater =new SourceCodeCreater(className,fields);
var sourceCode = sourceCodeCreater.Create();
Console.WriteLine(sourceCode);
var typeName = sourceCodeCreater.TypeName;
CSharpCodeProvider p = new CSharpCodeProvider();
CompilerParameters param = new CompilerParameters(new string[] { "System.dll" });
CompilerResults rel = p.CompileAssemblyFromSource(param, sourceCode);
Type t = rel.CompiledAssembly.GetType(typeName);
编译代码获得Type
运行一下
static void Main(string[] args)
{
var className = "Person";
var fields =new Dictionary<Type,string>();
fields.Add(typeof(string),"name");
fields.Add(typeof(int),"age");
SourceCodeCreater sourceCodeCreater =new SourceCodeCreater(className,fields);
var sourceCode = sourceCodeCreater.Create();
Console.WriteLine(sourceCode);
var typeName = sourceCodeCreater.TypeName;
CSharpCodeProvider p = new CSharpCodeProvider();
CompilerParameters param = new CompilerParameters(new string[] { "System.dll" });
CompilerResults rel = p.CompileAssemblyFromSource(param, sourceCode);
Type t = rel.CompiledAssembly.GetType(typeName);
Console.WriteLine(t.FullName);
foreach (var f in t.GetFields())
{
Console.WriteLine("Type:{0} Name:{1}",f.FieldType,f.Name);
}
Console.Read();
}
参考
QQ群:1022985150 VX:kklldog 一起探讨学习.NET技术
作者:Agile.Zhou(kklldog)
出处:http://www.cnblogs.com/kklldog/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。