使用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();
}
参考
使用CodeDom动态生成类型的更多相关文章
- 反射的妙用:C#通过反射动态生成类型继承接口并实现
起因 最近想自己鼓捣个RPC,想着简化RPC调用方式,直接申明接口,然后根据接口的属性去配置RPC调用的相关信息.有一种说法叫申明式调用. 简单来说就是,申明一个interface,动态继承并实例化, ...
- Roslyn 编译器Api妙用:动态生成类并实现接口
在上一篇文章中有讲到使用反射手写IL代码动态生成类并实现接口. 反射的妙用:C#通过反射动态生成类型继承接口并实现 有位网友推荐使用 Roslyn 去脚本化动态生成,今天这篇文章就主要讲怎么使用 Ro ...
- c# 表达式目录树拷贝对象(根据对象类型动态生成表达式目录树)
表达式目录树,在C#中用Expression标识,这里就不介绍表达式目录树是什么了,有兴趣可以自行百度搜索,网上资料还是很多的. 这里主要分享的是如何动态构建表达式目录树. 构建表达式目录树的代码挺简 ...
- 分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续3篇-导出时动态生成多Sheet EXCEL)
ExcelUtility 类库经过我(梦在旅途)近期不断的优化与新增功能,现已基本趋向稳定,功能上也基本可以满足绝大部份的EXCEL导出需求,该类库已在我们公司大型ERP系统全面使用,效果不错,今天应 ...
- 即使用ADO.NET,也要轻量级动态生成更新SQL,比Ormlite性能更高
先上测试结果: //测试1000次针对同一个表同一个字段更新,比Ormlite平均快2.34倍 //生成SQL+ExecuteNonQuery Ormlite 倍数 //6513ms 15158ms ...
- .Net 中的反射(动态创建类型实例) - Part.4
动态创建对象 在前面节中,我们先了解了反射,然后利用反射查看了类型信息,并学习了如何创建自定义特性,并利用反射来遍历它.可以说,前面三节,我们学习的都是反射是什么,在接下来的章节中,我们将学习反射可以 ...
- MVC5+EF6 入门完整教程13 -- 动态生成多级菜单
稍微有一定复杂性的系统,多级菜单都是一个必备组件. 本篇专题讲述如何生成动态多级菜单的通用做法. 我们不用任何第三方的组件,完全自己构建灵活通用的多级菜单. 需要达成的效果:容易复用,可以根据mode ...
- ABAP 动态生成内表的几种方法
最近要写个程序,既有更新的,也有删除的,需要涉及到很多系统表,如果一个表一个表进行更新或者删除太慢了,于是就想通过创建动态内表来实现这些功能,在网上找了一些资料,经过多次尝试,终于测试成功了.网上讲述 ...
- 利用StringList对象来管理这些动态生成的对象
如果程序需要动态创建大量的对象,那么我们可以利用StringList对象来管理这些动态生成的对象.1.创建StringList对象:OBJ := TStringList.Create; 2.保存动态生 ...
随机推荐
- linux内核分析作业6:分析Linux内核创建一个新进程的过程
task_struct结构: struct task_struct { volatile long state;进程状态 void *stack; 堆栈 pid_t pid; 进程标识符 u ...
- 微软“.Net社区虚拟大会”dotnetConf2015 第二天 无处不在的Xamarin
今天,微软召开了“.Net 社区虚拟大会”(dotnetConf),包括微软的 Scott Hanselman 和 Xamarin 的 Miguel De Icaza 在内的知名人士,都将在 Chan ...
- [译] 理解PHP内部函数的定义(给PHP开发者的PHP源码-第二部分)
文章来自:http://www.hoohack.me/2016/02/10/understanding-phps-internal-function-definitions-ch 原文:https:/ ...
- 当我谈 "加班有罪" 我在谈什么?
前言 PS. 本文只描述IT行业. 博客园果真人气比较高,我之前准备写个 "领域驱动系列",然后感觉大家不感兴趣,看来用的人不多,所以一直没动力续,但是昨天写了 [加班有罪] (h ...
- 64位Win7添加32位XP的共享打印机
windows无法连接到打印机 错误提示0x00000214 win7 64位系统,连接32位xp上的共享打印机,提示“操作失败,错误为 0x00000214”. 使用以下方法连接就没问题了: 开始— ...
- 短线技术MACD指标图解
1.通常DIF上穿0轴线的当天是中长线难得第一次买入的好时机,会引起场外资金的关注,如果上穿后MACD没有调头的迹象,则股价回调到5日均线附近为买入的好时机,必要时参考其他指标追涨.在0轴线以上形成2 ...
- hibernate注解方法使用总结(转)
原博文地址:http://blog.sina.com.cn/s/blog_935ebb670101dnre.html 1.类级别注解 @Entity 映射实体类 @Table 映射数据库 ...
- SSIS Data Flow优化
一,数据流设计优化 数据流有两个特性:流和在内存缓冲区中处理数据,根据数据流的这两个特性,对数据流进行优化. 1,流,同时对数据进行提取,转换和加载操作 流,就是在source提取数据时,转换组件处理 ...
- 【Win10应用开发】自定义打印选项
老周在前一篇烂文中已经给大伙伴们演示了如何打印UI元素,今天的烂文就向各位介绍一下,如何向打印对话框添加自定义选项.如果只是讲如何实现,会比较抽象,也比较枯燥,而且相当无聊,更是说不清楚,毕竟这打印A ...
- javascript运动系列第四篇——抖动
× 目录 [1]原理介绍 [2]代码实现 [3]实例应用 前面的话 在运动系列中,前面分别介绍了匀速运动.变速运动和曲线运动.下面介绍一种特殊的运动形式——抖动 原理介绍 抖动其实是往复运动的一种特殊 ...