性能优化-列表类型转换(ConvertList<TSource, TResult>)
之前,在项目中看到过一段通用列表类型转换的代码,直接的实现便是用反射。大概看了下,它用在领域模型转DTO和SOA接口中契约实体的转换等等。首先,它使用了反射,其次,还是在循环中使用,便有了优化的想法。
方法原型如:public static List<TResult> ConvertList<TSource, TResult>(List<TSource> source) where TResult : new(),下面贴出代码。说明一下,在此我没有任何的贬义,这段代码可能比较老,其次在项目中,首先是实现功能,如果当时没有更好的实现,就先实现功能,后面有时间可以在优化,毕竟项目有时间节点,个人自己平衡哈。
public class ObjectConvertHelperOld
{
/// <summary>
/// 转换单个对象为另外一种类型对象
/// </summary>
/// <typeparam name="TSource">待转换的源对象类型</typeparam>
/// <typeparam name="TResult">转换的目标对象类型</typeparam>
/// <param name="source">待转换的源对象</param>
/// <returns>转换的目标对象</returns>
public static TResult ConvertObject<TSource, TResult>(TSource source) where TResult : new()
{
TResult result = new TResult(); Type sourceType = source.GetType();
Type resultType = result.GetType(); PropertyInfo[] resultProperties = resultType.GetProperties(
BindingFlags.Public | BindingFlags.Instance); if (resultProperties != null && resultProperties.Length > )
{
foreach (PropertyInfo resultProperty in resultProperties)
{
if (resultProperty.PropertyType.IsGenericType)
{
continue;
} PropertyInfo sourceProperty = sourceType.GetProperty(resultProperty.Name); bool isMatched = sourceProperty != null &&
(!sourceProperty.PropertyType.IsGenericType) &&
(sourceProperty.PropertyType == resultProperty.PropertyType); if (isMatched)
{
object currentValue = sourceProperty.GetValue(source, null);
resultProperty.SetValue(result, currentValue, null);
} }
}
return result;
} /// <summary>
/// 转换列表对象为另外一种列表对象
/// </summary>
/// <typeparam name="TSource">待转换的源对象类型</typeparam>
/// <typeparam name="TResult">转换的目标对象类型</typeparam>
/// <param name="source">待转换的源对象</param>
/// <returns>转换的目标对象</returns>
public static List<TResult> ConvertList<TSource, TResult>(List<TSource> source) where TResult : new()
{
return source.ConvertAll<TResult>(ConvertObject<TSource, TResult>);
} }
从上面代码可以看出,它核心是从TSource类型到TResult类型转换,转换中,1、区分大小写,2、以TResult类型中的属性为准,如果源类型中有,就赋值过来(实际上是取两个实体属性的交集),3、还考虑字段是否是泛型等等。。。
如果熟悉Expression Tree的同学,可能就会想到,可以优化反射调用。老赵博客《表达式树与反射调用》系列中有详细实现,推荐大家去看看,绝对干货!我很多这方面的知识从这里学到的,非常感谢啊!
说一下优化思路,其实也不是什么思路了。利用类型字典和LambdaExpression的Compile方法为每组转换的类型缓存一个动态生成的委托。那么委托的调用和直接方法调用性能几乎是一样了。
有时候可能会涉及平台之间的契约转换,比如之前做的一个项目,在.net中调用第三方java的接口,java定义的契约,它的字段命名是camelCasing(小写开头,如:payAmount),我们之间约定是使用http post 数据传输格式采用json字符串,那么json字符串区分大小写,我们两边都使用序列化反序列化等。我这边就需要两份契约了,一份是第三方接口契约实体,采用小写开头命名,第二份是内部契约,采用.net 命名规则PascalCasing,来定义实体属性。这里将内部契约实体转换成第三方契约实体,PayAmount到payAmount的对应转换。
之前考虑的是属性映射区分大小写还是不区分,由调用者参数控制,对于这个需求,简化一下就是属性映射不区分大小写啦,2、以TResult类型中的字段为准(取交集),3、TResult对象的创建是在转换内部创建的,有没有可能这个TResult对象列表已经存在?对于为什么选择属性映射不区分大小写,考虑有二,1、.net中实体中属性的定义,一般不定义重名的(userId,UserId)2、对于TSource中字段和TResult字段完全相同,也不影响啊
优化代码如下:
public static class ObjectConvertHelper
{
private class InnerConversion<TSource, TResult>
{
private static readonly Func<TSource, TResult> s_convert;
static InnerConversion()
{
s_convert = BuildConvert();
}
private static Func<TSource, TResult> BuildConvert()
{//(x)=>new TResult{P1=x.p1,P2=x.p2,...};
var paramExp = Expression.Parameter(typeof(TSource), "x");
var sourcePropertyInfos = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead && p.CanWrite);
var resultPropertyInfos = typeof(TResult).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead && p.CanWrite);
var resultPropertyBindings = new List<MemberBinding>(resultPropertyInfos.Count());
foreach (var item in resultPropertyInfos)
{
//不区分大小写
PropertyInfo piIgnoreCase = sourcePropertyInfos.Where(x => string.Compare(x.Name, item.Name, true) == ).FirstOrDefault();
if (piIgnoreCase != null)
{
resultPropertyBindings.Add((MemberBinding)Expression.Bind(item, Expression.Property(paramExp, piIgnoreCase))
);
}
}
var body = Expression.MemberInit( // object initializer
Expression.New(typeof(TResult)), // ctor
resultPropertyBindings // property assignments
);
return Expression.Lambda<Func<TSource, TResult>>(body, paramExp).Compile();
}
/// <summary>
/// 将TSource实体转换到TResult实体(属性匹配规则:1、不区分大小写,2、两个实体属性取交集,3、TResult实体内部创建)
/// </summary>
public static Func<TSource, TResult> Convert
{
get
{
return s_convert;
}
}
} /// <summary>
/// 将一种类型列表转换为另一种类型列表
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="sourceList"></param>
/// <returns></returns>
public static IList<TResult> ConvertList<TSource, TResult>(IList<TSource> sourceList)
where TSource : class
where TResult : class,new()
{
if (sourceList == null) { throw new ArgumentNullException("sourceList"); }
if (sourceList.Count == )
{
return new List<TResult>();
}
return sourceList.Select(p => InnerConversion<TSource, TResult>.Convert(p)).ToList();
} public static TResult Convert<TSource, TResult>(TSource source)
where TSource : class
where TResult : class,new()
{
if (source == null) { throw new ArgumentNullException("source"); }
return InnerConversion<TSource, TResult>.Convert(source);
}
/// <summary>
/// 浅拷贝实体
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static T ShallowClone<T>(T source) where T : class,new()
{
if (source == null) { throw new ArgumentNullException("source"); }
return InnerConversion<T, T>.Convert(source);
}
}
类型字典(Type Dictionary):泛型类中的静态字段,会根据泛型的具体类型如InnerConversion<SourceEntity, ResultEntity>有一份对应的静态字段,具体可看装配脑袋文章等。由于系统中的类型个数有限,这样为每种类型缓存一份转换方法,可以说一劳永逸。动态生成委托Func<TSource, TResult>,很强大,可以做很多通用的功能,就像CLR帮我们写代码一样,可参考之前的《Expression Tree实践之通用Parse方法------"让CLR帮我写代码"》等。好了,下面来对比一下两者的性能吧,使用老赵的CodeTimer,测试代码如下:
class SourceEntity
{
public int UserId { get; set; }
public string name { get; set; } public string p3 { get; set; }
public string p4 { get; set; }
public string p5 { get; set; }
public string p6 { get; set; }
public string p7 { get; set; }
public string p8 { get; set; }
public string p9 { get; set; }
public string p10 { get; set; }
public string p11 { get; set; } public string sourceOther { get; set; }
} class ResultEntity
{
public int UserId { get; set; }
public string Name { get; set; } public string P3 { get; set; }
public string P4 { get; set; }
public string P5 { get; set; }
public string P6 { get; set; }
public string P7 { get; set; }
public string P8 { get; set; }
public string P9 { get; set; }
public string P10 { get; set; }
public string P11 { get; set; } public string Comment { get; set; }
} static List<SourceEntity> GenerateSources(int length)
{
List<SourceEntity> result = new List<SourceEntity>();
for (int i = ; i < length; i++)
{
result.Add(new SourceEntity {
UserId=i,
name="stevey"+i,
p3=i.ToString(),
p4 = i.ToString(),
p5 = i.ToString(),
p6 = i.ToString(),
p7 = i.ToString(),
p8 = i.ToString(),
p9 = i.ToString(),
p10 = i.ToString(),
p11 = i.ToString(),
sourceOther="sourceOther"
});
}
return result;
}
public static void Main(string[] args)
{
List<SourceEntity> sourceList = GenerateSources();//生成测试数据 CodeTimer.Initialize();
//对于10W个元素集合执行10次转换,如下
CodeTimer.Time("常规反射实现的类型转换", , () => {
var resultList = ObjectConvertHelperOld.ConvertList<SourceEntity, ResultEntity>(sourceList);
}); CodeTimer.Time("优化过的类型转换",, () => {
var resultList = ObjectConvertHelper.ConvertList<SourceEntity, ResultEntity>(sourceList);
}); Console.ReadKey();
}
在Release模式下编译后,对于10W个元素的列表执行10次结果如下:
如果执行次数增加,还会有更大的差距,因为已经为类型缓存了委托,就几乎相当于直接方法调用了,而老的实现每次都需要反射SetValue。但是动态编译生成委托,这个过程比较耗时,可以作为初始化,只执行一次,后面就一劳永逸了。
执行100次的结果如下:
好了,就写到这里吧,如有不正之处还请指正,相互交流,共同进步~~
性能优化-列表类型转换(ConvertList<TSource, TResult>)的更多相关文章
- 列表类型转换(ConvertList<TSource, TResult>)
性能优化-列表类型转换(ConvertList<TSource, TResult>) 2013-12-16 16:55 by stevey, 426 阅读, 7 评论, 收藏, 编辑 之前 ...
- Web前端性能优化总结——如何提高网页加载速度
一.提高网页加载速度的必要性 国际知名的一组来自Jupiter Research的数据显示:购物者在访问网站过程中的不满会导致销售损失和品牌受损,其中 77%的人将不再访问网站 ,62%的人不再从该网 ...
- [daily][optimize] 一个小python程序的性能优化 (python类型转换函数引申的性能优化)
前天,20161012,到望京面试.第四个职位,终于进了二面.好么,结果人力安排完了面试时间竟然没有通知我,也没有收到短信邀请.如果没有短信邀请门口的保安大哥是不让我进去大厦的.然后,我在11号接到了 ...
- Vue2.5开发去哪儿网App 城市列表开发之 兄弟组件间联动及列表性能优化
一, 兄弟组件间联动 1. 点击城市字母,左侧对应显示 给遍历的 字母 添加一个点击事件: Alphabet.vue @click="handleLetterClick" ha ...
- h5列表页的性能优化
//0.还原状态 caoke.loading=false $(".loadbtn").text("点击加载更多") //1 还没有任何数据的情况 if(data ...
- 摘:JavaScript性能优化小知识总结
原文地址:http://www.codeceo.com/article/javascript-performance-tips.html JavaScript的性能问题不容小觑,这就需要我们开发人员在 ...
- 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化
第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...
- s性能优化方面的小知识
总结的js性能优化方面的小知识 前言 一直在学习javascript,也有看过<犀利开发Jquery内核详解与实践>,对这本书的评价只有两个字犀利,可能是对javascript理解的还不够 ...
- [转]C#程序性能优化
C#程序性能优化 1.显式注册的EvenHandler要显式注销以避免内存泄漏 将一个成员方法注册到某个对象的事件会造成后者持有前者的引用.在事件注销之前,前者不会被垃圾回收. private v ...
随机推荐
- BZOJ 3901 棋盘游戏 解题报告
这题有个重要性质: 我们设 Flag[i][j] 表示 (i, j) 是否被奇数个操作所覆盖, 也就是操作次数对 2 取模. 设 x = (n + 1) / 2. 那么对于所有的合法的操作方案, 令 ...
- [XJOI NOI2015模拟题13] A 神奇的矩阵 【分块】
题目链接:XJOI NOI2015-13 A 题目分析 首先,题目定义的这种矩阵有一个神奇的性质,第 4 行与第 2 行相同,于是第 5 行也就与第 3 行相同,后面的也是一样. 因此矩阵可以看做只有 ...
- Oracle SQL tuning 步骤
Oracle SQL tuning 步骤 SQL是的全称是Structured Query Language(结构化查询语言).SQL是一个在80年代中期被使用的工业标准数据库查询语言.不要把SQL语 ...
- js高手
http://kb.cnblogs.com/page/173798/ http://kb.cnblogs.com/page/121539/ http://blog.jobbole.com/9648/ ...
- JavaScript 判断是否为undefined
if (typeof(reValue) == "undefined") { alert("undefined"); }
- Windows server 2008下开启telnet功能
今天在windows server 2008 R2下使用telnet 来测试端口是否可以连接,结果发现如下错误:
- 关于百度地图API的地图坐标转换问题
原文:关于百度地图API的地图坐标转换问题 我在之前的文章利用html5获取经纬度并且在百度地图中显示位置中使用了百度地图的API来显示html5获取的地理位置,在文中我说过这样的话,我说百度地图的准 ...
- addlinkedserver
http://blog.sina.com.cn/s/blog_5321db9d0100f89g.html --创建链接服务器 exec sp_addlinkedserver 'ITSV ', ' ' ...
- windows下面虚拟主机
1. wondows xp + apmserv 5.2.6 端口8088 2.httpd.config文件 1. ServerRoot "D:/APMServ/APMServ5.2.6/A ...
- Tomcat工作原理详解
一.Tomcat背景 自从JSP发布之后,推出了各式各样的JSP引擎.Apache Group在完成GNUJSP1.0的开发以后,开始考虑在SUN的JSWDK基础上开发一个可以直接提供Web服务的JS ...