引言

    哈喽。大家好,好久不见,最近遇到了一个场景,就是在FrameWork的asp.net mvc中,有个系统里面使用的是EntityFramework的框架,在这个框架里,提供了一个SqlQuery的方法,这个方法很好用啊,以至于在EFCORE8里面又添加了回来,不过不知道性能怎么样,我遇到的场景是通过SqlQuery查询的时候,转换很慢,我估计那背后大概率是使用反射造成的, 因为我的查询可能有上十万,甚至更多,就导致了这个转换的过程及其耗时,以至于刚开始我是想通过Emit等方式去实现一个高性能转换,可是到最后没有去弄,因为我用了DataCommand去查询,最后循环DataReader来实现硬赋值,这样性能是最好,一下减少了好多秒,提升了80%,但也给了我一个灵感,一个实现简易的类型转换的灵感,所以在上周我就把代码写了出来,不过由于工作的忙碌,今天才开始写博客,接下来就呈上。

    对了,有关EMIT和表达式树的知识,诸位可以看我之前的博客表达式树,IL。其中IL有两合集。

EMIT

    众所周知,我们的c#代码在编译器编译,都会编译成IL代码,最后再去通过JIT转化为机器码,运行在系统中去的,所以IL代码的性能是比c#代码高的,同时,学习的成本,编写的成本也是机器高,我也是在自己感兴趣,瞎琢磨,就会了一丝丝皮毛,很多人说IL难写,其实,对于代码中的Opcodes那些,我只记一些常用的,对于剩下的,我都是在写的时候才去看文档,总之呢,要学的东西有很多,但掌握了学习的方法,才是持之以恒的手段。

    接下来,就呈上IL代码,分为两部分,一个是List转List,一个是实体转实体的。

    在这几个例子中,所有的前提都是实体的属性名称是一样的,如果需要扩展类型不一样,或者哪些不转换,从哪个属性转换到哪个属性,就需要各位自己去扩展了,本来我是想写这些的,,但是懒癌犯了,哈哈哈哈,需要各位看官自己动手了,以下代码,除了反射,其他的我都加了注释,反射大家都看得懂。

    在下面的第一个方法,我们定义了执行转换集合的方法,并返回了一个委托,我们在实际开发中,都可以返回委托,最终可以将方法缓存起来,这样在后续的时候直接调用,性能提升爆炸,因为你每次创建Emit方法的时候,耗时也会挺长的,在有时候,像哪些主流的Mapper,他们内部肯定都做了缓存。下面的集合转集合,大致的原理代码就是定义一个方法ConvertToType,返回类型是List<TR>,入参是List<T>,然后定义循环的开始结束变量,以及最终返回结果集,还有循环内部的时候,我们创建的变量,最终将这个变量添加到返回的结果集中,在往下就是拿入参集合的数量,定义循环开始和结束的label,在往下走就是,初始化返回值集合,赋值给本地的localRes变量,将0赋值给开始循环的变量,也就是for(int i=0),下面就是给结束的循环值赋值为入参集合的Count。

    下面的代码每行基本都有注释,所以我在这里也不做过多的讲解。

    集合和单个的区别就在于集合是多了一个循环的主体,其他都和单个是一样的,以及集合的代码块中,我没有添加try catch的代码块。

    internal class EmitExecute<T, TR> : IExecute<T, TR> where T : class where TR : class, new()
{
public Func<List<T>, List<TR>> ExecuteList()
{
var dynamicMethod = new DynamicMethod("ConvertToType", typeof(List<TR>), new Type[] { typeof(List<T>) });
#region 变量定义
var ilMethod = dynamicMethod.GetILGenerator();
var localBegin = ilMethod.DeclareLocal(typeof(int));//定义循环开始变量
var localEnd = ilMethod.DeclareLocal(typeof(int));//结束变量
var localres = ilMethod.DeclareLocal(typeof(List<TR>));//返回值
var localSignleRes = ilMethod.DeclareLocal(typeof(TR));//变量
var countMethod = typeof(List<T>).GetProperty("Count").GetGetMethod();
#endregion
var labelXunhuan = ilMethod.DefineLabel();//定义循环主体
var labelEnd = ilMethod.DefineLabel();//定义结束标签主体
#region 实例化返回值
ilMethod.Emit(OpCodes.Nop);
ilMethod.Emit(OpCodes.Newobj, typeof(List<TR>).GetConstructor(Type.EmptyTypes));//初始化返回值变量
ilMethod.Emit(OpCodes.Stloc, localres);//结果赋值给返回值变量变量
#endregion
#region 给循环的起始变量赋值 0是开始,count是获取的入参的集合的count
ilMethod.Emit(OpCodes.Ldc_I4_0);//将0加载到栈上
ilMethod.Emit(OpCodes.Stloc, localBegin);//给循环的开始值begin赋值0
ilMethod.Emit(OpCodes.Ldarg_0);//加载入参
ilMethod.Emit(OpCodes.Callvirt, countMethod);//将入参的count加载到栈上
ilMethod.Emit(OpCodes.Stloc, localEnd);//给结束变量赋值为集合的count
ilMethod.Emit(OpCodes.Br, labelXunhuan);//无条件跳转到循环的label,即开始循环
#endregion
#region 循环结束标签
ilMethod.MarkLabel(labelEnd);//标记这段代码是labelend的代码 ilMethod.Emit(OpCodes.Ldloc, localres);//加载返回值变量
ilMethod.Emit(OpCodes.Ret);//返回
#endregion
#region 循环主体
ilMethod.MarkLabel(labelXunhuan);//标记是labelbegin的代码
ilMethod.Emit(OpCodes.Ldloc, localBegin);//从栈中加载开始变量
ilMethod.Emit(OpCodes.Ldloc, localEnd);//从栈中加载结束变量
ilMethod.Emit(OpCodes.Bge, labelEnd);//比较第0个是否小于等于第一个 //ilMethod.Emit(OpCodes.Call, typeof(List<People>).GetMethod("Add"));
ilMethod.Emit(OpCodes.Newobj, typeof(TR).GetConstructor(Type.EmptyTypes));//创建people的实例化
ilMethod.Emit(OpCodes.Stloc, localSignleRes);//结果赋值people变量
var properties = typeof(T).GetProperties().Where(s => typeof(TR).GetProperties().Any(a => a.Name == s.Name)).ToList();
if (properties != null)
{
foreach (var item in properties)
{
var getMethod = typeof(T).GetProperty(item.Name).GetGetMethod();//获取list【i】的species属性
var setMethod = typeof(TR).GetProperty(item.Name).GetSetMethod();
ilMethod.Emit(OpCodes.Ldloc, localSignleRes);//加载定义好的people
ilMethod.Emit(OpCodes.Ldarg_0);//加载入参
ilMethod.Emit(OpCodes.Ldloc, localBegin);//加载循环的开始变量
ilMethod.Emit(OpCodes.Callvirt, typeof(List<T>).GetMethod("get_Item"));// 调用 List<Animal>.get_Item 方法,将结果压入栈顶
ilMethod.Emit(OpCodes.Callvirt, getMethod);//拿到getitem的返回值的species属性
ilMethod.Emit(OpCodes.Call, setMethod);//给people的species属性赋值
}
} //ilMethod.Emit(OpCodes.Ldloc, localPeople);//加载定义好的people
//ilMethod.Emit(OpCodes.Ldarg_0);//加载入参
//ilMethod.Emit(OpCodes.Ldloc, localBegin);//加载循环的开始变量
//ilMethod.Emit(OpCodes.Callvirt, typeof(List<T>).GetMethod("get_Item"));// 调用 List<Animal>.get_Item 方法,将结果压入栈顶
//ilMethod.Emit(OpCodes.Callvirt, getAMethod);//拿到getitem的返回值的species属性
//ilMethod.Emit(OpCodes.Call, property);//给people的species属性赋值 ilMethod.Emit(OpCodes.Ldloc, localres);//加载返回值
ilMethod.Emit(OpCodes.Ldloc, localSignleRes);//加载people ilMethod.Emit(OpCodes.Call, typeof(List<TR>).GetMethod("Add"));//将people添加道返回值
ilMethod.Emit(OpCodes.Ldc_I4_1);//将1加载到栈上
ilMethod.Emit(OpCodes.Ldloc, localBegin);//从栈中加载开始变量
ilMethod.Emit(OpCodes.Add);//相加
ilMethod.Emit(OpCodes.Stloc, localBegin);//结果赋值给本地0个局部变量
ilMethod.Emit(OpCodes.Br, labelXunhuan);
#endregion
var func = dynamicMethod.CreateDelegate<Func<List<T>,List<TR>>>();
return func;
} public Func<T, TR> ExecuteSingle()
{
var dynamicMethod = new DynamicMethod("ConvertToType", typeof(TR), new Type[] { typeof(T) });
var method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });//输出字符串
var ilMethod = dynamicMethod.GetILGenerator();
var localRes = ilMethod.DeclareLocal(typeof(TR));//变量
var ex = typeof(Exception);
var localEx = ilMethod.DeclareLocal(ex);
var str = typeof(Exception).GetMethod("ToString");
ilMethod.Emit(OpCodes.Nop);
ilMethod.Emit(OpCodes.Newobj, typeof(TR).GetConstructor(Type.EmptyTypes));//初始化返回值变量 new tr();
ilMethod.Emit(OpCodes.Stloc, localRes);//结果赋值给返回值变量变量var tr=new tr();
var properties = typeof(T).GetProperties().Where(s => typeof(TR).GetProperties().Any(a => a.Name == s.Name)).ToList();
if (properties != null)
{
ilMethod.BeginExceptionBlock();//try
foreach (var item in properties)
{
var getMethod = typeof(T).GetProperty(item.Name).GetGetMethod();//获取list【i】的species属性
var setMethod = typeof(TR).GetProperty(item.Name).GetSetMethod();
ilMethod.Emit(OpCodes.Ldloc, localRes);//加载定义好的people
ilMethod.Emit(OpCodes.Ldarg_0);//加载入参
ilMethod.Emit(OpCodes.Callvirt, getMethod);//拿到getitem的返回值的species属性
ilMethod.Emit(OpCodes.Call, setMethod);//给people的species属性赋值
}
ilMethod.BeginCatchBlock(ex);
ilMethod.Emit(OpCodes.Stloc, localEx);// 将异常的ex赋值给我们的本地变量ex
ilMethod.Emit(OpCodes.Ldloc, localEx);//加载ex变量
ilMethod.Emit(OpCodes.Callvirt, str);//调用tostring()
ilMethod.Emit(OpCodes.Call, method);//调用console.writeline方法打印出异常信息
ilMethod.Emit(OpCodes.Newobj, typeof(TR).GetConstructor(Type.EmptyTypes));
ilMethod.Emit(OpCodes.Stloc, localRes);//结果赋值给返回值变量变量
ilMethod.EndExceptionBlock();
}
ilMethod.Emit(OpCodes.Ldloc, localRes);//加载返回值
ilMethod.Emit(OpCodes.Ret);
var func = dynamicMethod.CreateDelegate<Func<T, TR>>();
return func;
}
}
}

Expression

    接下来,是表达式树的实现方式,表达式树的其实和Emit的我感觉都差不多,不过和emit相比,肯定大家都喜欢写Expression,毕竟是c#代码,写起来比较舒适,在下面代码就是定义了入参的source,以及从source那指定索引的item,以及返回值res,异常的定义和异常的message,在下面就是循环两个公共属性的信息,调用bind方法,从item的里面拿出sourceproperty的属性和targetproperty绑定,然后给res初始化,设置他的count为source的count,并且判断如果source长度是0,就直接返回一个空的集合,下面有一个构造循环的方法,判断index是否小于集合的count,如果不成立,直接调用break标签,也就是我们的break关键字,如果成立,拿出对应的item,然后调用了MemberInit方法,初始化了一个TR,然后调用Add方法添加到返回的结果集合中,这样就实现了一个一个的转换,最后将所有的表达式整合为一个代码块,通过Block再加入Try Catch,最终编译成一个Func的委托。下面的单个的原理也是一样的。

  internal class ExpressionExecute<T, TR> : IExecute<T, TR> where T : class where TR : class, new()
{
private List<PropertyInfo> properties;
public ExpressionExecute()
{
properties = typeof(TR).GetProperties().Where(s => typeof(T).GetProperties().Any(a => a.Name == s.Name)).ToList();//获取相同的属性名称
}
public Func<List<T>, List<TR>> ExecuteList()
{ var sourceParam = Expression.Parameter(typeof(List<T>), "source");//定义入参
var itemVar = Expression.Variable(typeof(T), "item");//定义从集合获取的item变量
var resVar = Expression.Variable(typeof(List<TR>), "res");//定义返回的结果变量
var ex = Expression.Variable(typeof(Exception), "ex");
var msg = Expression.Property(ex, "Message"); var method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
var memberBindings = new List<MemberBinding>();//memberbind
var listExpressions = new List<Expression>();//expression foreach (var property in properties)
{
var sourceProperty = typeof(T).GetProperty(property.Name);
var targetProperty = typeof(TR).GetProperty(property.Name); if (sourceProperty.PropertyType == targetProperty.PropertyType)//判断类型
{
memberBindings.Add(Expression.Bind(targetProperty, Expression.Property(itemVar, sourceProperty)));//相等直接bind add
}
} listExpressions.Add(Expression.Assign(resVar, Expression.New(typeof(List<TR>))));//设置res=new list<tr>
listExpressions.Add(Expression.Assign(Expression.Property(resVar, "Capacity"), Expression.Property(sourceParam, "Count"))); //设置res.count==入参的count var indexVar = Expression.Variable(typeof(int), "index");//循环索引
var breakLabel = Expression.Label("LoopBreak");//break标签 listExpressions.Add(Expression.IfThen(
Expression.Equal(Expression.Property(sourceParam, "Count"), Expression.Constant(0)),//如果source.cout==0
Expression.Return(breakLabel, resVar, typeof(List<TR>)) //直接 break 返回res
));
//构造循环
var innerLoop = CreateLoop(indexVar, sourceParam, itemVar,resVar,memberBindings,breakLabel);
listExpressions.AddRange(new Expression[] { innerLoop , Expression.Label(breakLabel) , resVar });
var body = Expression.Block(new[] { itemVar, indexVar, resVar }, listExpressions);//整合一个代码块
var tryCatch = Expression.TryCatch(body, new CatchBlock[] { Expression.Catch(ex, Expression.Block(Expression.Call(null, method, msg), Expression.New(typeof(List<TR>)))) });//try catch
var lambda = Expression.Lambda<Func<List<T>, List<TR>>>(tryCatch, sourceParam);
return lambda.Compile();
}
private LoopExpression CreateLoop(ParameterExpression indexVar,ParameterExpression sourceParam,ParameterExpression itemVar,ParameterExpression resVar,List<MemberBinding> memberBindings,LabelTarget breakLabel)
{
var addMethod = typeof(List<TR>).GetMethod("Add");//结果集的add方法
return Expression.Loop(
Expression.IfThenElse(//如果index <count 进入block
Expression.LessThan(indexVar, Expression.Property(sourceParam, "Count")),
Expression.Block(
Expression.Assign(itemVar, Expression.Property(sourceParam, "Item", indexVar)),//设置item=source【index】
Expression.Call(resVar, addMethod, Expression.MemberInit(Expression.New(typeof(TR)), memberBindings)),//调用res.add方法
Expression.PostIncrementAssign(indexVar)//index=index+1
),
Expression.Break(breakLabel)//如果index<count不成立,直接中断循环
)
);
}
public Func<T, TR> ExecuteSingle()
{
var express = Expression.Parameter(typeof(T), "source");
var memberBindings = new List<MemberBinding>();//memberbing
var list = new List<Expression>();
var action = new Func<string, string>(s => {
return typeof(TR).GetProperty(s).Name;
});//根据属性名称获取属性,由于property第二个参数必须 string or method,下方就只有他通过call的方式获取属性的name名称
int ipropertydx = 0;//相同属性遍历所以
memberBindings.Clear(); var peoples = Expression.New(typeof(TR));//创建新的tr实例
foreach (var item in properties)//属性遍历
{
var property = Expression.Property(peoples, item.Name);//获取tr实例属性
var memberInfo = typeof(TR).GetMember(item.Name).FirstOrDefault(); // 获取 MemberInfo 对象
if (memberInfo != null)
{
var assignment = Expression.Bind(//将属性和source[index].属性关联起来
memberInfo, Expression.Property( //获取source的属性
express,
//调用上面的委托返回要拿的属性名称也就是A.name=B.name
Expression.Lambda<Func<string>>(
Expression.Call(Expression.Constant(action.Target), action.Method, //调用action拿名称
Expression.Property(
Expression.ArrayIndex(Expression.Constant(properties.ToArray()), Expression.Constant(ipropertydx)), "Name")//获取properties的第ipropertydx的name名称
)
).Compile() //编译为func《string》委托
()
));
memberBindings.Add(assignment);//将每个people的每个属性赋值的assignment添加进去
ipropertydx++;
}
}
var memberInit = Expression.MemberInit(peoples, memberBindings);//将peoples初始化 初始化绑定的每一个成员
var func = Expression.Lambda<Func<T, TR>>(memberInit, express).Compile();//编译为委托
return func;
}
}

反射

    反正,反射是很耗时的,少量情况还好,大量并不建议使用,虽然很好用,这里我也只是做一个例子,让我自己用,肯定优选前面两个,这个代码更不用讲了,懂得都懂,

  internal class ReflectionExecute<T, TR> : IExecute<T, TR> where T : class where TR : class,new()
{
public Func<List<T>, List<TR>> ExecuteList()
{
var res = new Func<List<T>, List<TR>>(s =>
{
var resList = new List<TR>();
var properties = typeof(T).GetProperties().Where(s => typeof(TR).GetProperties().Any(a => a.Name == s.Name)).ToList();//获取相同的属性名称
foreach (var item in s)
{
var tr = new TR();
foreach (var itemproperty in properties)
{
var val = itemproperty.GetValue(item, null);
if (val != null)
{
var setProperty = typeof(TR).GetProperty(itemproperty.Name);
setProperty.SetValue(tr, val);
}
}
resList.Add(tr);
}
return resList;
});
return res;
} public Func<T, TR> ExecuteSingle()
{
var res = new Func<T, TR>(s =>
{ var properties = typeof(TR).GetProperties().Where(s => typeof(T).GetProperties().Any(a => a.Name == s.Name)).ToList();//获取相同的属性名称
var tr = new TR();
foreach (var itemproperty in properties)
{
var val = itemproperty.GetValue(s, new object[] { });
if (val != null)
{
var setProperty = typeof(TR).GetProperty(itemproperty.Name);
setProperty.SetValue(tr, val);
}
}
return tr;
});
return res;
}
}

测试

    这玩意,我自己简单测试了一下,本想用benmark简单跑一下,但是麻烦,就代码自己测试了一下,在第一次构建表达式树的方法,会有些许耗时,但是在最后如果有缓存方法,那性能不必Emit差,总之,性能方面优选Emit和表达式树,反射就不做考虑。

总结

    赶鸭子上架,水了一篇博客,如有疑问,可以随时呼我,QQ934550201.

    代码地址:

    链接:https://pan.baidu.com/s/1vW9LPfYHmvk6Y08qEYA9sw
    提取码:vj34

    

【类型转换】使用c#实现简易的类型转换(Emit,Expression,反射)的更多相关文章

  1. ExpressionTree,Emit,反射

    ExpressionTree,Emit,反射 https://www.cnblogs.com/7tiny/p/9861166.html [前言] 前几日心血来潮想研究着做一个Spring框架,自然地就 ...

  2. JS 数据类型转换-转换函数、强制类型转换、利用js变量弱类型转换

    1. 转换函数: js提供了parseInt()和parseFloat()两个转换函数.前者把值转换成整数,后者把值转换成浮点数.只有对String类型调用这些方法,这两个函数才能正确运行:对其他类型 ...

  3. 常用类型转换 一.常用int和string类型转换

    常用类型转换 一.常用int类型转换1. int.parse(string) 这个类型只支持string类型 2.double doubleType = Int32.MaxValue + 1;   i ...

  4. 类型和原生函数及类型转换(三:终结js类型转换)

    Number() parseInt() parseFloat() Boolean() String() toString() 一.显式类型转换 -------Number()函数把对象的值转换为数字. ...

  5. 再看ExpressionTree,Emit,反射创建对象性能对比

    [前言] 前几日心血来潮想研究着做一个Spring框架,自然地就涉及到了Ioc容器对象创建的问题,研究怎么高性能地创建一个对象.第一联想到了Emit,兴致冲冲写了个Emit创建对象的工厂.在做性能测试 ...

  6. Emit优化反射(属性的设置与获取)

    在频繁的通过反射来设置和获取属性的值时是比较耗时的,本章通过Emit技术优化反射来提高获取和设置属性值的效率 一.实现代码: /// <summary> /// 设置器委托 /// < ...

  7. Android简易注解View(java反射实现)

    一.引言 Android中通过findViewById在布局文件中找到需要的View,加入一个Activity里面有许多的View需要初始化,那将是一件很繁琐的事情.当然Google一下你会发现有很多 ...

  8. 使用 IL 实现类型转换

    在之前的文章中,我大致介绍过一些类型间的隐式和显式类型转换规则.但当时并未很仔细的研究过<CSharp Language Specification>,因此实现并不完整.而且只部分解决了类 ...

  9. struts2类型转换

    1. Struts2中的类型转换 我们知道通过HTTP提交到后台的数据,都是字符串的形式,而我们需要的数据类型当然不只字符串类型一种.所以,我们需要类型转换! 在Struts2中,类型转换的概念除了用 ...

  10. C++_系列自学课程_第_11_课_类型转换_《C++ Primer 第四版》

    上次说了关于表达式的一些内容,说到还有一些关于数据类型转换的内容,今天我们接着八一八C++中的数据类型转换. 一.隐式类型转换 在表达式中,有些操作符可以对多种类型的操作数进行操作, 例如 + 操作符 ...

随机推荐

  1. redis基本数据类型 Hash

    Hash 类型 Hash类型的常见命令 HSET key field value: 添加或者修改hash类型key的field的值HGET key field: 获取一个hash类型key的field ...

  2. ubuntu22.04 安装nginx 卸载

    1.使用apt-get 安装nginx(得机器能联网才行) # 切换到root用户 # 切换到root用户 sudo -i # 更新apt源 apt-get update# 安装nginx apt-g ...

  3. MySQL系列之——SQL介绍、常用SQL分类、数据类型、表属性、字符集、DDL应用、DCL应用、DML应用(增删改)、DQL应用(select )、元数据信息、show命令

    文章目录 一 SQL介绍 二 常用SQL分类 2.1 客户端命令 三 数据类型.表属性.字符集 3.1 数据类型 3.1.1 作用 3.1.2 种类 3.2 表属性 3.2.1 列属性 3.2.2 表 ...

  4. Go 基础之基本数据类型

    Go 基础之基本数据类型 目录 Go 基础之基本数据类型 一.整型 1.1 平台无关整型 1.1.1 基本概念 1.1.2 分类 有符号整型(int8~int64) 无符号整型(uint8~uint6 ...

  5. Go 语言开发环境搭建

    Go 语言开发环境搭建 目录 Go 语言开发环境搭建 一. GO 环境安装 1.1 下载 1.2 Go 版本的选择 1.3 安装 1.3.1 Windows安装 1.3.2 Linux下安装 1.3. ...

  6. Java多线程笔记全过程(一)

    一.多线程最基础的基本概念 一个程序最少需要一个进程,而一个进程最少需要一个线程. 我们常说的高并发,也并不全是线程级别的并发,在很多开发语言中,比如PHP,很常见的都是进程级别的并发.但是在Java ...

  7. CF862B

    题目简化和分析: 这是一道较为经典的二分图染色题. 二分图的基本概念 但这题让我们求得是完全二分图. 什么是完全二分图 \(cnt_{1}\) 表示染成颜色种类为 \(1\) 的个数. \(cnt_{ ...

  8. Chromium 通过IDL方式添加扩展API

    基于chromium103版本 1. 自定义扩展API接口 chromium默认扩展api接口中有chrome.runtime.*,和chrome.send.*等,现在我们就仿照chrome.runt ...

  9. 一文详解贝叶斯优化(Bayesian Optimization)原理

    参考资料: Expected Improvement formula for Bayesian Optimisation 通俗科普文:贝叶斯优化与SMBO.高斯过程回归.TPE 理解贝叶斯优化 A T ...

  10. 18. 从零开始编写一个类nginx工具, 主动式健康检查源码实现

    wmproxy wmproxy将用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,后续将实现websocket代理, 内外网穿透等, 会将实现过程分享出来, 感 ...