測試背景(更詳細介紹,請參照這篇文章,感謝樓主無私奉獻):
有時候爲了將實體的字段值賦值給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");
結果如下:
結論:
通過上面的測試可以看出,兩者行能相差較大,表達式目錄樹更適合做數據複製。
謝謝打賞