性能优化-列表类型转换(ConvertList<TSource, TResult>)

2013-12-16 16:55 by stevey, 426 阅读, 7 评论, 收藏编辑

  之前,在项目中看到过一段通用列表类型转换的代码,直接的实现便是用反射。大概看了下,它用在领域模型转DTO和SOA接口中契约实体的转换等等。首先,它使用了反射,其次,还是在循环中使用,便有了优化的想法。

方法原型如:public static List<TResult> ConvertList<TSource, TResult>(List<TSource> source) where TResult : new(),下面贴出代码。说明一下,在此我没有任何的贬义,这段代码可能比较老,其次在项目中,首先是实现功能,如果当时没有更好的实现,就先实现功能,后面有时间可以在优化,毕竟项目有时间节点,个人自己平衡哈。

 

  从上面代码可以看出,它核心是从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) == 0).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 == 0)
{
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,测试代码如下:

在Release模式下编译后,对于10W个元素的列表执行10次结果如下:

如果执行次数增加,还会有更大的差距,因为已经为类型缓存了委托,就几乎相当于直接方法调用了,而老的实现每次都需要反射SetValue。但是动态编译生成委托,这个过程比较耗时,可以作为初始化,只执行一次,后面就一劳永逸了。

执行100次的结果如下:

  好了,就写到这里吧,如有不正之处还请指正,相互交流,共同进步~~

 
 

列表类型转换(ConvertList<TSource, TResult>)的更多相关文章

  1. 性能优化-列表类型转换(ConvertList<TSource, TResult>)

    之前,在项目中看到过一段通用列表类型转换的代码,直接的实现便是用反射.大概看了下,它用在领域模型转DTO和SOA接口中契约实体的转换等等.首先,它使用了反射,其次,还是在循环中使用,便有了优化的想法. ...

  2. json序列化.xml序列化.图片转base64.base64转图片.生成缩略图.IEnumerable<TResult> Select<TSource, TResult>做数据转换的五种方式

     JSON序列化 /// <summary> /// JSON序列化 /// </summary> public static class SPDBJsonConvert { ...

  3. c#列表操作

    Enumerable[从元数据]   //        // 摘要:         //     从序列的开头返回指定数量的连续元素.        //        // 参数:        ...

  4. c# 分页 PaginatedList<TResult>

    using System; using System.Collections.Generic; using System.Linq; namespace Microestc.PaginatedList ...

  5. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  6. C#基础:LINQ 查询函数整理

    1.LINQ 函数   1.1.查询结果过滤 :where() Enumerable.Where() 是LINQ 中使用最多的函数,大多数都要针对集合对象进行过滤,因此Where()在LINQ 的操作 ...

  7. C#中的LINQ

    从自己的印象笔记里面整理出来,排版欠佳.见谅!   1.LINQ: 语言集成查询(Language Integrated Query) 实例: var q=      from c in catego ...

  8. C# 语言规范_版本5.0 (第7章 表达式)

    1. 表达式 表达式是一个运算符和操作数的序列.本章定义语法.操作数和运算符的计算顺序以及表达式的含义. 1.1 表达式的分类 一个表达式可归类为下列类别之一: 值.每个值都有关联的类型. 变量.每个 ...

  9. 查询表达式和LINQ to Objects

    查询表达式实际上是由编译器“预处理”为“普通”的C#代码,接着以完全普通的方式进行编译.这种巧妙的发式将查询集合到了语言中,而无须把语义改得乱七八糟 LINQ的介绍 LINQ中的基础概念 降低两种数据 ...

随机推荐

  1. Echarts Jqplot嵌extjs4 windows 装配方法

    js组件绘图终于是画在一个指定id的div或dom元素中. 在项目中有可能须要画在 Extjs容器中,研究了一下,能够通过下面的思路实现,方法跟大家共享下: 1.首先做一个容器,把此内容加入到wind ...

  2. 允许Android随着屏幕转动的控制自由转移到任何地方(附demo)

    在本文中,Android ViewGroup/View流程,及经常使用的自己定义ViewGroup的方法.在此基础上介绍动态控制View的位置的三种方法,并给出最佳的一种方法. 一.ViewGroup ...

  3. 【C++模版之旅】项目中一次活用C++模板(traits)的经历

    曾经曾在一个项目中碰到过一个挺简单的问题,但一时又不能用普通常规的方法去非常好的解决,最后通过C++模板的活用,通过traits相对照较巧妙的攻克了这个问题.本文主要想重现问题发生,若干解决方式的比較 ...

  4. [Error]EOL while scanning string literal

    有一个经常性的工作项目.需要一天的一些表数据到外部接口,但最近总是异常.今天检查的原因. 第一本地和测试环境中测试程序是没有问题,有网络环境只会在日志中抛出一个异常.产生主要的例外是推定异常数据. , ...

  5. LINUX2.4.x网络安全框架

       在分析LINUX2.4.x网络安全的实现之前先简介一下它里面包括的几个重要概念:netfilter.iptables.match.target.nf_sockopt_ops.网络安全功能点的实现 ...

  6. SQL Profile (总结4)--使用演示示例

    前提:sql profile工具的相关视图 dba_sql_profile 10g: sys.sqlprof$attr  &  sqlprof$ 11g: sys.sqlobj$data  & ...

  7. Grunt和Gulp构建工具在Visual Studio 2015中的高效的应用

    Grunt和Gulp构建工具在Visual Studio 2015中的高效的应用 Grunt和Gulp是Javascript世界里的用来做自动压缩.Typescript编译.代码质量lint工具.cs ...

  8. 项目管理实践 -- 健身小管家(Fitness housekeeper)的管理(4)

    提前几天把检查更新的功能完成了.

  9. java23种设计模式详解(转)

    设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  10. poj 2513 Colored Sticks(欧拉路径+并检查集合+特里)

    题目链接:poj 2513 Colored Sticks 题目大意:有N个木棍,每根木棍两端被涂上颜色.如今给定每一个木棍两端的颜色.不同木棍之间拼接须要颜色同样的 端才干够.问最后是否能将N个木棍拼 ...