测试背景(更详细介绍,请参照这篇文章,感谢楼主无私奉献):
有时候为了将实体的字段值赋值给DTO的字段,需要实现复制功能(字段较多的时候,直接硬编码会很痛苦)。一般可以通过硬编码、反射、序列化反序列化实现,但效率都相对较低。今天我们通过表达式树的方式,实现复制功能,顺便和序列化反序列化方法做个性能比对。
DTO:数据传输对象,我们经常用到的一个东西。有时候我们称之为的ViewModel也属于其中之一。以往,我们总是复制实体类型的一些字段 然后单独创建这些对象。
实体类如下:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
DTO如下:
public class PersonDTO
{
public int Id { get; set; }
public string Name { get; set; }
}
序列化反序列化如下:
using Newtonsoft.Json;
public static class ObjectExtension
{
/// <summary>
/// 序列化、反序列化
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <typeparam name="TOut"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static TOut Copy<TIn, TOut>(TIn t)
{
return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(t));
}
}
表达式目录树如下:
using System.Linq.Expressions;
using System.Runtime.Serialization.Formatters.Binary;
/// <summary>
/// 表达式目录树 拷贝方法,必须是同结构类
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <typeparam name="TOut"></typeparam>
public class ExpressionGenercMapper<TIn, TOut>
{
private static Func<TIn, TOut> _Func = null;
static ExpressionGenercMapper()
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
List<MemberBinding> memberBindings = new List<MemberBinding>();
foreach (var item in typeof(TOut).GetProperties())
{
MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindings.Add(memberBinding);
}
foreach (var item in typeof(TOut).GetFields())
{
MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindings.Add(memberBinding);
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindings.ToArray());
Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
{
parameterExpression
});
_Func = lambda.Compile();
}
public static TOut Trans(TIn t)
{
return _Func(t);
}
}
测试如下:
Person person = new Person()
{
Id = 1,
Name = "张三"
};
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 100_000; i++)
{
PersonDTO personClone = ObjectExtension.Copy<Person, PersonDTO>(person);
}
stopwatch.Stop();
TimeSpan timespan = stopwatch.Elapsed;
double millisecondsOne = timespan.TotalMilliseconds;
Stopwatch stopwatch1 = new Stopwatch();
stopwatch1.Start();
for (int i = 0; i < 100_000; i++)
{
PersonDTO personClone = ExpressionGenercMapper<Person, PersonDTO>.Trans(person);
}
stopwatch1.Stop();
TimeSpan timespan1 = stopwatch1.Elapsed;
double millisecondsTwo = timespan1.TotalMilliseconds;
Console.WriteLine($"序列化:{millisecondsOne}s");
Console.WriteLine($"目录树:{millisecondsTwo}s");
结果如下:
结论:
通过上面的测试可以看出,两者行能相差较大,表达式目录树更适合做数据复制。
谢谢打赏