话题:两个类属性字段一样怎么转化:将Student 转化为 StudentCopy
这里先提供两个方法:
一
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Task1
{
public class SerializeMapper
{
public static TOut Trans (TIn t )
{
return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(t));
}
}
}
二
public static TOut Trans (TIn t )
{
TOut tout = Activator.CreateInstance();
foreach (var prop in typeof(TOut).GetProperties())
{
var propinfo = typeof(TIn).GetProperty(prop.Name);
if (propinfo != null)
{
prop.SetValue(tout, propinfo.GetValue(t));
}
}
foreach (var field in typeof(TOut).GetFields())
{
var fieldfo = typeof(TIn).GetProperty(field.Name);
if (fieldfo != null)
{
field.SetValue(tout, fieldfo.GetValue(t));
}
}
return tout;
}
以上两个都可以实现,但是以上两种方法 一个是通过反射找属性和字段, 一个通过反射转字符串 都是性能不友好的
有没有即通用又性能良好的方式呢:
思路:利用表达式目录树来实现,其实反编译工具可以将下面的快捷方式反编译为 普通的目录树声明方式,直接抄下来改一下就好
Expression< Func> trans = s => new StudentCopy{ Id = s.Id, Name = s.Name,Age = s.Age};
StudentCopy studentCopy = trans.Compile()(new Student { Id = 1, Name = "张三", Age = 20 });
需要明白这种方式和如下代码一个意思:
var student = new Student { Id = 1, Name = "张三", Age = 20 };
var studentCopy = new StudentCopy { Id = student.Id, Name = student.Name, Age = student.Age };
这里我们实现一下:
public class ExpressionMapper
{
private static Dictionary Dic = new Dictionary();
public static TOut Trans (TIn t )
{
//模仿对象
//Expression< Func> trans = s => new StudentCopy{ Id = s.Id, Name = s.Name,Age = s.Age};
string key = string.Format("funkey_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);
if (!Dic.ContainsKey(key))
{
ParameterExpression s = Expression.Parameter(typeof(TIn), "s");
List memberBindingList = new List();
foreach (var item in typeof(TOut).GetProperties())
{
MemberExpression propExp = Expression.Property(s, typeof(TIn).GetProperty(item.Name));//得到 原类 中的属性 即 s.Id, s.Name,s.Age
MemberBinding memberBinding = Expression.Bind(item, propExp);// 将原类的属性绑定到 目标类属性上 即 Id = s.Id, Name = s.Name,Age = s.Age
memberBindingList.Add(memberBinding);
}
foreach (var item in typeof(TOut).GetFields())
{
MemberExpression field = Expression.Property(s, typeof(TIn).GetProperty(item.Name));
MemberBinding memberBinding = Expression.Bind(item, field);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());//创建一个Body
Expression> tempExp = Expression.Lambda>(memberInitExpression, s);//创建表达式目录树
var lamada = tempExp.Compile();//获取委托实例
Dic.Add(key, lamada);//加入静态字典
}
return ((Func)Dic[key])(t);
}
}
三种方法都可以实现目的,但是使用目录树的方式是性能最好的
虽然第一次难免需要使用反射查找属性和字段,但是有静态Dictionary作为缓存
性能好且通用
和直接写代码复制没有区别,但是实现代码相对复杂一点,可以看见都可以顺利完成
这里还可以结合以前学习的 静态字典 因为 Dictionary 类 和 静态字典的效率是没发比的 如下:静态字典出来的每个类都是独立的不用像
Dictionary 一样还要去查找 这里的查找会损耗大量性能
public class ExpressionGenericMapper
{
private static Func FuncCache =null;
static ExpressionGenericMapper(){
ParameterExpression s = Expression.Parameter(typeof(TIn), "s");
List memberBindingList = new List();
foreach (var item in typeof(TOut).GetProperties())
{
MemberExpression propExp = Expression.Property(s, typeof(TIn).GetProperty(item.Name));//得到 原类 中的属性 即 s.Id, s.Name,s.Age
MemberBinding memberBinding = Expression.Bind(item, propExp);// 将原类的属性绑定到 目标类属性上 即 Id = s.Id, Name = s.Name,Age = s.Age
memberBindingList.Add(memberBinding);
}
foreach (var item in typeof(TOut).GetFields())
{
MemberExpression field = Expression.Property(s, typeof(TIn).GetProperty(item.Name));
MemberBinding memberBinding = Expression.Bind(item, field);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());//创建一个Body
Expression> tempExp = Expression.Lambda>(memberInitExpression, s);//创建表达式目录树
FuncCache = tempExp.Compile();//获取委托实例
}
public static TOut Trans(TIn t )
{
return FuncCache(t);
}
}
这里将每一种方法都执行100000次看一下效率:
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 100000; i++)
{
var student = new Student { Id = 1, Name = "张三", Age = 20 };
var studentCopy5 = new StudentCopy { Id = student.Id, Name = student.Name, Age = student.Age };
}
stopwatch.Stop();
var Common = stopwatch.ElapsedMilliseconds;
Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
for (int i = 0; i < 100000; i++)
{
var studentCopy = ReflectionMapper.Trans(new Student { Id = 1, Name = "张三", Age = 20 });
}
stopwatch2.Stop();
var Refection = stopwatch2.ElapsedMilliseconds;
Stopwatch stopwatch3 = new Stopwatch();
stopwatch3.Start();
for (int i = 0; i < 100000; i++)
{
var studentCopy2 = SerializeMapper.Trans(new Student { Id = 1, Name = "张三", Age = 20 });
}
stopwatch3.Stop();
var Serialize = stopwatch3.ElapsedMilliseconds;
Stopwatch stopwatch4 = new Stopwatch();
stopwatch4.Start();
for (int i = 0; i < 100000; i++)
{
var studentCopy3 = ExpressionMapper.Trans(new Student { Id = 1, Name = "张三", Age = 20 });
}
stopwatch4.Stop();
var Expression = stopwatch4.ElapsedMilliseconds;
Stopwatch stopwatch5 = new Stopwatch();
stopwatch5.Start();
for (int i = 0; i < 100000; i++)
{
var studentCopy4 = ExpressionGenericMapper.Trans(new Student { Id = 1, Name = "张三", Age = 20 });
}
stopwatch5.Stop();
var ExpressionGeneric = stopwatch5.ElapsedMilliseconds;
Console.WriteLine("Common:" + Common);
Console.WriteLine("Refection:" + Refection);
Console.WriteLine("Serialize:" + Serialize);
Console.WriteLine("Expression:" + Expression);
Console.WriteLine("ExpressionGeneric:" + ExpressionGeneric);
结果:可以看见 直接硬编码的效率是最好的,但是没有通用性,序列化是性能最差的,
静态字典加目录树的方式是性能和硬编码处于同一数量级,且有通用性的方式