.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();
}

参考

MSDN CodeDom

使用CodeDom动态生成类型的更多相关文章

  1. 反射的妙用:C#通过反射动态生成类型继承接口并实现

    起因 最近想自己鼓捣个RPC,想着简化RPC调用方式,直接申明接口,然后根据接口的属性去配置RPC调用的相关信息.有一种说法叫申明式调用. 简单来说就是,申明一个interface,动态继承并实例化, ...

  2. Roslyn 编译器Api妙用:动态生成类并实现接口

    在上一篇文章中有讲到使用反射手写IL代码动态生成类并实现接口. 反射的妙用:C#通过反射动态生成类型继承接口并实现 有位网友推荐使用 Roslyn 去脚本化动态生成,今天这篇文章就主要讲怎么使用 Ro ...

  3. c# 表达式目录树拷贝对象(根据对象类型动态生成表达式目录树)

    表达式目录树,在C#中用Expression标识,这里就不介绍表达式目录树是什么了,有兴趣可以自行百度搜索,网上资料还是很多的. 这里主要分享的是如何动态构建表达式目录树. 构建表达式目录树的代码挺简 ...

  4. 分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续3篇-导出时动态生成多Sheet EXCEL)

    ExcelUtility 类库经过我(梦在旅途)近期不断的优化与新增功能,现已基本趋向稳定,功能上也基本可以满足绝大部份的EXCEL导出需求,该类库已在我们公司大型ERP系统全面使用,效果不错,今天应 ...

  5. 即使用ADO.NET,也要轻量级动态生成更新SQL,比Ormlite性能更高

    先上测试结果: //测试1000次针对同一个表同一个字段更新,比Ormlite平均快2.34倍 //生成SQL+ExecuteNonQuery Ormlite 倍数 //6513ms 15158ms ...

  6. .Net 中的反射(动态创建类型实例) - Part.4

    动态创建对象 在前面节中,我们先了解了反射,然后利用反射查看了类型信息,并学习了如何创建自定义特性,并利用反射来遍历它.可以说,前面三节,我们学习的都是反射是什么,在接下来的章节中,我们将学习反射可以 ...

  7. MVC5+EF6 入门完整教程13 -- 动态生成多级菜单

    稍微有一定复杂性的系统,多级菜单都是一个必备组件. 本篇专题讲述如何生成动态多级菜单的通用做法. 我们不用任何第三方的组件,完全自己构建灵活通用的多级菜单. 需要达成的效果:容易复用,可以根据mode ...

  8. ABAP 动态生成内表的几种方法

    最近要写个程序,既有更新的,也有删除的,需要涉及到很多系统表,如果一个表一个表进行更新或者删除太慢了,于是就想通过创建动态内表来实现这些功能,在网上找了一些资料,经过多次尝试,终于测试成功了.网上讲述 ...

  9. 利用StringList对象来管理这些动态生成的对象

    如果程序需要动态创建大量的对象,那么我们可以利用StringList对象来管理这些动态生成的对象.1.创建StringList对象:OBJ := TStringList.Create; 2.保存动态生 ...

随机推荐

  1. ASP.NET关于对excel数据导入到数据库

    //*******************************C#(UI代码)********************************/ Web界面上放一个FileUpload 名称为:F ...

  2. 玩转JavaScript OOP[4]——实现继承的12种套路

    概述 在之前的文章中,我们借助构造函数实现了"类",然后结合原型对象实现了"继承",并了解了JavaScript中原型链的概念. 理解这些内容,有助于我们更深入 ...

  3. Google Chrome调试js入门

    平常在开发过程中,经常会接触到前端页面.那么对于js的调试那可是家常便饭,不必多说.最近一直在用火狐的Firebug,但是不知道怎么的不好使了.网上找找说法,都说重新安装狐火浏览器就可以了,但是我安装 ...

  4. [python] 安装numpy+scipy+matlotlib+scikit-learn及问题解决

    这篇文章主要讲述Python如何安装Numpy.Scipy.Matlotlib.Scikit-learn等库的过程及遇到的问题解决方法.最近安装这个真是一把泪啊,各种不兼容问题和报错,希望文章对你有所 ...

  5. Atitit 图像处理知识点体系知识图谱 路线图attilax总结 v4 qcb.xlsx

    Atitit 图像处理知识点体系知识图谱 路线图attilax总结 v4 qcb.xlsx 分类 图像处理知识点体系 v2 qb24.xlsx 分类 分类 理论知识 图像金字塔 常用底层操作 卷积扫描 ...

  6. Struts2-修改数据

    <body> 用户信息:<br><br> <% List<User> lu = (List<User>)request.getAttr ...

  7. SQL 笔记 By 华仔

    -------------------------------------读书笔记------------------------------- 笔记1-徐 最常用的几种备份方法 笔记2-徐 收缩数据 ...

  8. 模拟ajax的 script请求

    /** * 模拟ajax的 script请求 * @param {[type]} options [description] * @return {[type]} [description] */ f ...

  9. 【转】如何让你的Android SDK下载或者升级快如闪电

    准备学习Android开发,但是Android SDK Manager界面都刷不出来,今天看到了一篇文章,顿时就解决了,原文地址http://qichaochen.github.io/2014/12/ ...

  10. Gridview里添加合计行

    ShowFooter="true" ; ; ; protected void gvIncomeYG_RowDataBound(object sender, GridViewRowE ...