使用CodeDom动态生成类型

.NET 3.5的时候加入了匿名类型这个特性,我们可以直接使用 new {name="abc"} 来直接生成一个对象。这个特性现在应用的地方很多,比如dapper的查询参数都是用匿名对象。

其实匿名对象也不是真的没有名称,编译器在编译后自动会生成一个Type。我们看看IL就知道了。

使用CodeDom动态生成类型

编译器会自动生成一个叫做<>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();
}

使用CodeDom动态生成类型

参考

MSDN CodeDom

上一篇:Jarvis OJ- [XMAN]level2/3_x64-Writeup——64位简单栈溢出


下一篇:Git与GitHub学习笔记(五)一次提交失败的记录