對象類型映射轉換常用方法
開發過程中經常會遇到對象類型之間的轉換映射,例如Model和ViewModel之間的映射綁定,下面總結幾種常見的轉換方式。事先准備兩個類:
CheckFile
和 CheckFileModel
public class CheckFile
{
public string Id { get; set; }
public string FileTitle { get; set; }
public string Author { get; set; }
public DateTime? CreatTime;
}
public class CheckFileModel
{
public string Id { get; set; }
public string FileTitle { get; set; }
public string Author { get; set; }
public DateTime? CreatTime;
public string Source { get; set; }
}
1. 使用強類型賦值綁定
CheckFile checkFile = new CheckFile
{
Id = "123",
FileTitle = "對象類型轉換映射",
Author = "張三",
CreatTime = DateTime.Now
};
CheckFileModel fileModel = new CheckFileModel
{
Id = checkFile.Id,
FileTitle = checkFile.FileTitle,
Author = checkFile.Author,
CreatTime = checkFile.CreatTime
};
2. 使用泛型+反射
/// <summary>
/// 泛型+反射
/// </summary>
/// <typeparam name="TOut"></typeparam>
/// <typeparam name="TIn"></typeparam>
/// <param name="objIn"></param>
/// <returns></returns>
public static TOut TypeConvert<TOut, TIn>(TIn objIn)
{
Type tOut = typeof(TOut);
Type tIn = typeof(TIn);
TOut outModel = Activator.CreateInstance<TOut>();
// 屬性賦值
foreach (var prop in tOut.GetProperties())
{
var propInfo = tIn.GetProperty(prop.Name);
if (propInfo != null)
{
var inValue = propInfo.GetValue(objIn);
prop.SetValue(outModel, inValue);
}
}
// 字段賦值
foreach (var field in tOut.GetFields())
{
var fieldInfo = tIn.GetField(field.Name);
if (fieldInfo != null)
{
var inValue = fieldInfo.GetValue(objIn);
field.SetValue(outModel, inValue);
}
}
return outModel;
}
3. 使用Json序列化轉換
/// <summary>
/// Json序列化轉換
/// </summary>
/// <typeparam name="TOut"></typeparam>
/// <typeparam name="TIn"></typeparam>
/// <param name="objIn"></param>
/// <returns></returns>
public static TOut SerializeConvert<TOut, TIn>(TIn objIn)
{
string inString = JsonConvert.SerializeObject(objIn);
TOut outModel = JsonConvert.DeserializeObject<TOut>(inString);
return outModel;
}
4. 使用AutoMapper序列化轉換
/// <summary>
/// AutoMapper序列化轉換
/// </summary>
/// <typeparam name="TOut"></typeparam>
/// <typeparam name="TIn"></typeparam>
/// <param name="objIn"></param>
/// <returns></returns>
public static TOut AutoMapperConvert<TOut, TIn>(TIn objIn)
{
// 初始化
Mapper.Initialize(n => n.CreateMap<TIn, TOut>());
TOut outModel = Mapper.Map<TIn, TOut>(objIn);
return outModel;
}
5. 使用Expression表達式目錄樹轉換
/// <summary>
/// Expression表達式目錄樹轉換
/// </summary>
/// <typeparam name="TOut"></typeparam>
/// <typeparam name="TIn"></typeparam>
/// <param name="objIn"></param>
/// <returns></returns>
public static TOut ExpressionConvert<TOut, TIn>(TIn objIn)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
List<MemberBinding> memberBindingList = new List<MemberBinding>();
// 綁定屬性
PropertyInfo[] outPropertyInfos = typeof(TOut).GetProperties();
foreach (var prop in outPropertyInfos)
{
PropertyInfo inPropertyInfo = typeof(TIn).GetProperty(prop.Name);
if (inPropertyInfo != null)
{
MemberExpression property = Expression.Property(parameterExpression, inPropertyInfo);
MemberBinding memberBinding = Expression.Bind(prop, property);
memberBindingList.Add(memberBinding);
}
}
// 綁定字段
FieldInfo[] outFieldInfos = typeof(TOut).GetFields();
foreach (var field in outFieldInfos)
{
FieldInfo inFieldInfo = typeof(TIn).GetField(field.Name);
if (inFieldInfo != null)
{
MemberExpression fieldInfo = Expression.Field(parameterExpression, inFieldInfo);
MemberBinding memberBinding = Expression.Bind(field, fieldInfo);
memberBindingList.Add(memberBinding);
}
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
{
parameterExpression
});
Func<TIn, TOut> func = lambda.Compile();
return func.Invoke(objIn);
}
//----------------------------------------------------------
// 等價於構造一個下面的表達式
Expression<Func<CheckFile, CheckFileModel>> lambda = p => new CheckFileModel
{
Id = p.Id,
FileTitle = p.FileTitle,
Author = p.Author,
CreatTime = p.CreatTime
};
lambda.Compile().Invoke(checkFile);
表達式目錄樹介紹
每個矩形框為一個節點(表達式類型),節點有多種類型,對於而這個 a*b+2 的幾個節點:
- a,b是參數,類型為ParameterExpression
- +,*,為二元運符,類型為BinaryExpression
- 2為常量,類型為ConstantExpression
Expression<Func<int, int, int>> exp1 = (a, b) => a * b+2;
//兩個參數
ParameterExpression a = Expression.Parameter(typeof(int), "a");
ParameterExpression b = Expression.Parameter(typeof(int), "b");
//求積
BinaryExpression Multi=Expression.Multiply(a,b);
//常量
ConstantExpression x2 = Expression.Constant(2);
//求和
BinaryExpression Add = Expression.Add(Multi, x2);
//創建一個表示lambda表達式的對象
LambdaExpression lexp = Expression.Lambda<Func<int, int, int>>(Add, a, b);
//查看表達式
Console.WriteLine(lexp.ToString());
//簡單使用
Expression<Func<int, int, int>> lexp = Expression.Lambda<Func<int, int, int>>(Add, a, b);
Func<int, int, int> fun = lexp.Compile();
Console.WriteLine(fun(3,5));
出處:https://blog.csdn.net/qq_31176861/article/details/86551996
=======================================================================================
C# 高性能對象映射(表達式樹實現)
前言
上篇簡單實現了對象映射,針對數組,集合,嵌套類並沒有給出實現,這一篇繼續完善細節。
開源對象映射類庫映射分析
1.AutoMapper
實現原理:主要通過表達式樹Api 實現對象映射
優點: .net功能最全的對象映射類庫。
缺點:當出現復雜類型和嵌套類型時性能直線下降,甚至不如序列化快
2.TinyMapper
實現原理:主要通過Emit 實現對象映射
優點:速度非常快。在處理復雜類型和嵌套類型性能也很好
缺點:相對AutoMapper功能上少一些,Emit的實現方案,在代碼閱讀和調試上相對比較麻煩,而表達式樹直接觀察 DebugView中生成的代碼結構便可知道問題所在
3. 本文的對象映射庫
針對AutoMapper 處理復雜類型和嵌套類型時性能非常差的情況,自己實現一個表達式樹版的高性能方案
實現對象映射庫的過程
構造測試類
public class TestA { public int Id { get; set; } public string Name { get; set; } public TestC TestClass { get; set; } public IEnumerable<TestC> TestLists { get; set; } } public class TestB { public int Id { get; set; } public string Name { get; set; } public TestD TestClass { get; set; } public TestD[] TestLists { get; set; } } public class TestC { public int Id { get; set; } public string Name { get; set; } public TestC SelfClass { get; set; } } public class TestD { public int Id { get; set; } public string Name { get; set; } public TestD SelfClass { get; set; } }
1.初步實現
利用表達式樹給屬性賦值 利用 Expresstion.New構造 var b=new B{};
public static Func<TSource, TTarget> GetMap<TSource, TTarget>() { var sourceType = typeof(TSource); var targetType = typeof(TTarget); //構造 p=> var parameterExpression = Expression.Parameter(sourceType, "p"); //構造 p=>new TTarget{ Id=p.Id,Name=p.Name }; var memberBindingList = new List<MemberBinding>(); foreach (var sourceItem in sourceType.GetProperties()) { var targetItem = targetType.GetProperty(sourceItem.Name); if (targetItem == null || sourceItem.PropertyType != targetItem.PropertyType) continue; var property = Expression.Property(parameterExpression, sourceItem); var memberBinding = Expression.Bind(targetItem, property); memberBindingList.Add(memberBinding); } var memberInitExpression = Expression.MemberInit(Expression.New(targetType), memberBindingList); var lambda = Expression.Lambda<Func<TSource, TTarget>>(memberInitExpression, parameterExpression); Console.WriteLine("lambda: " + lambda); return lambda.Compile(); }
調用如下
class Program { static void Main(string[] args) { var testA = new TestA { Id = 1, Name = "張三" }; var func = TestClass.ExpresstionHelper.GetMap<TestA, TestB>(); TestB testB = func(testA); Console.WriteLine($"testB.Id={testB.Id},testB.Name={testB.Name}"); Console.ReadLine(); } }
輸出結果
總結:此方法需要調用前需要手動編譯下,然后再調用委托沒有緩存委托,相對麻煩。
2.緩存實現
利用靜態泛型類緩存泛型委托
public class DataMapper<TSource, TTarget> { private static Func<TSource, TTarget> MapFunc { get; set; } public static TTarget Map(TSource source) { if (MapFunc == null) MapFunc = ExpresstionHelper.Map<TSource, TTarget>();//方法在上邊 return MapFunc(source); } }
調用方法
static void Main(string[] args) { var testA2 = new TestA { Id = 2, Name = "張三2" }; TestB testB2 = DataMapper<TestA, TestB>.Map(testA2);//委托不存在時自動生成,存在時調用靜態緩存 Console.WriteLine($"testB.Id={testB2.Id},testB.Name={testB2.Name}"); Console.ReadLine(); }
輸出結果
總結:引入靜態泛型類能解決泛型委托緩存提高性能,但是有兩個問題 1.當傳入參數為null時 則會拋出空引用異常 2.出現復雜類型上述方法便不能滿足了
3.解決參數為空值和復雜類型的問題
首先先用常規代碼實現下帶有復雜類型賦值的情況
1 public TestB GetTestB(TestA testA) 2 { 3 TestB testB; 4 if (testA != null) 5 { 6 testB = new TestB(); 7 testB.Id = testA.Id; 8 testB.Name = testA.Name; 9 if (testA.TestClass != null) 10 { 11 testB.TestClass = new TestD(); 12 testB.TestClass.Id = testA.TestClass.Id; 13 testB.TestClass.Name = testA.TestClass.Name; 14 } 15 } 16 else 17 { 18 testB = null; 19 } 20 return testB; 21 }
將上面的代碼翻譯成表達式樹
public static Func<TSource, TTarget> Map<TSource, TTarget>() { var sourceType = typeof(TSource); var targetType = typeof(TTarget); //Func委托傳入變量 var parameter = Expression.Parameter(sourceType); //聲明一個返回值變量 var variable = Expression.Variable(targetType); //創建一個if條件表達式 var test = Expression.NotEqual(parameter, Expression.Constant(null, sourceType));// p==null; var ifTrue = Expression.Block(GetExpression(parameter, variable, sourceType, targetType)); var IfThen = Expression.IfThen(test, ifTrue); //構造代碼塊 var block = Expression.Block(new[] { variable }, parameter, IfThen, variable); var lambda = Expression.Lambda<Func<TSource, TTarget>>(block, parameter); return lambda.Compile(); } private static List<Expression> GetExpression(Expression parameter, Expression variable, Type sourceType, Type targetType) { //創建一個表達式集合 var expressions = new List<Expression>(); expressions.Add(Expression.Assign(variable, Expression.MemberInit(Expression.New(targetType)))); foreach (var targetItem in targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite)) { var sourceItem = sourceType.GetProperty(targetItem.Name); //判斷實體的讀寫權限 if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic) continue; var sourceProperty = Expression.Property(parameter, sourceItem); var targetProperty = Expression.Property(variable, targetItem); //判斷都是class 且類型不相同時 if (targetItem.PropertyType.IsClass && sourceItem.PropertyType.IsClass && targetItem.PropertyType != sourceItem.PropertyType) { if (targetItem.PropertyType != targetType)//不處理嵌套循環的情況 { //由於類型是class 所以默認值是null var testItem = Expression.NotEqual(sourceProperty, Expression.Constant(null, sourceItem.PropertyType)); var itemExpressions = GetExpression(sourceProperty, targetProperty, sourceItem.PropertyType, targetItem.PropertyType); var ifTrueItem = Expression.Block(itemExpressions); var IfThenItem = Expression.IfThen(testItem, ifTrueItem); expressions.Add(IfThenItem); continue; } } //目標值類型時 且兩者類型不一致時跳過 if (targetItem.PropertyType != sourceItem.PropertyType) continue; expressions.Add(Expression.Assign(targetProperty, sourceProperty)); } return expressions; }
總結:此方案,運用 Expression.IfThen(testItem, ifTrueItem) 判斷空值問題,通過遞歸調用 GetExpression()方法,處理復雜類型。
但是:針對嵌套類仍然不能解決。因為表達式樹是在實際調用方法之前就生成的,在沒有實際的參數值傳入之前,生成的表達式是不知道有多少層級的。
有個比較low的方案是,預先設定嵌套層級為10層,然后生成一個有10層 if(P!=null) 的判斷。如果傳入的參數層級超過10層了呢,就得手動調整生成的樹,此方案也否決。
最后得出的結論只能在表達式中動態調用方法。
4.最終版本
通過動態調用方法解決嵌套類,代碼如下

using System.ComponentModel.DataAnnotations.Schema; using System.Linq.Expressions; using System.Reflection; using static System.Linq.Expressions.Expression; public static class ExprMapper<TSource, TTarget> where TSource : class where TTarget : class { private readonly static Func<TSource, TTarget> MapFunc = GetMapFunc(); private readonly static Action<TSource, TTarget> MapAction = GetMapAction(); /// <summary> /// 將對象TSource轉換為TTarget /// </summary> /// <param name="source"></param> /// <returns></returns> public static TTarget Map(TSource source) => MapFunc(source); /// <summary> /// 將對象TSource的值賦給給TTarget /// </summary> /// <param name="source"></param> /// <param name="target"></param> public static void Map(TSource source, TTarget target) => MapAction(source, target); private static List<TTarget> MapList(IEnumerable<TSource> sources) => sources.Select(MapFunc).ToList(); private static Func<TSource, TTarget> GetMapFunc() { var sourceType = typeof(TSource); var targetType = typeof(TTarget); //Func委托傳入變量 var parameter = Parameter(sourceType, "p"); var memberBindings = new List<MemberBinding>(); var targetTypes = targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite); foreach (var targetItem in targetTypes) { var sourceItem = sourceType.GetProperty(targetItem.Name); //判斷實體的讀寫權限 if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic) continue; //標注NotMapped特性的屬性忽略轉換 if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null) continue; var sourceProperty = Property(parameter, sourceItem); //當非值類型且類型不相同時 if (!sourceItem.PropertyType.IsValueType && sourceItem.PropertyType != targetItem.PropertyType) { //判斷都是(非泛型)class if (sourceItem.PropertyType.IsClass && targetItem.PropertyType.IsClass && !sourceItem.PropertyType.IsGenericType && !targetItem.PropertyType.IsGenericType) { var expression = GetClassExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType); memberBindings.Add(Bind(targetItem, expression)); } //集合數組類型的轉換 if (typeof(IEnumerable<>).IsAssignableFrom(sourceItem.PropertyType) && typeof(IEnumerable<>).IsAssignableFrom(targetItem.PropertyType)) { var expression = GetListExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType); memberBindings.Add(Bind(targetItem, expression)); } continue; } if (targetItem.PropertyType != sourceItem.PropertyType) continue; memberBindings.Add(Bind(targetItem, sourceProperty)); } //創建一個if條件表達式 var test = NotEqual(parameter, Constant(null, sourceType));// p==null; var ifTrue = MemberInit(New(targetType), memberBindings); var condition = Condition(test, ifTrue, Constant(null, targetType)); var lambda = Lambda<Func<TSource, TTarget>>(condition, parameter); return lambda.Compile(); } private static Action<TSource, TTarget> GetMapAction() { var sourceType = typeof(TSource); var targetType = typeof(TTarget); //Func委托傳入變量 var sourceParameter = Parameter(sourceType, "p"); var targetParameter = Parameter(targetType, "t"); //創建一個表達式集合 var expressions = new List<Expression>(); var targetTypes = targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite); foreach (var targetItem in targetTypes) { var sourceItem = sourceType.GetProperty(targetItem.Name); //判斷實體的讀寫權限 if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic) continue; //標注NotMapped特性的屬性忽略轉換 if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null) continue; var sourceProperty = Property(sourceParameter, sourceItem); var targetProperty = Property(targetParameter, targetItem); //當非值類型且類型不相同時 if (!sourceItem.PropertyType.IsValueType && sourceItem.PropertyType != targetItem.PropertyType) { //判斷都是(非泛型)class if (sourceItem.PropertyType.IsClass && targetItem.PropertyType.IsClass && !sourceItem.PropertyType.IsGenericType && !targetItem.PropertyType.IsGenericType) { var expression = GetClassExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType); expressions.Add(Assign(targetProperty, expression)); } //集合數組類型的轉換 if (typeof(IEnumerable<>).IsAssignableFrom(sourceItem.PropertyType) && typeof(IEnumerable<>).IsAssignableFrom(targetItem.PropertyType)) { var expression = GetListExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType); expressions.Add(Assign(targetProperty, expression)); } continue; } if (targetItem.PropertyType != sourceItem.PropertyType) continue; expressions.Add(Assign(targetProperty, sourceProperty)); } //當Target!=null判斷source是否為空 var testSource = NotEqual(sourceParameter, Constant(null, sourceType)); var ifTrueSource = Block(expressions); var conditionSource = IfThen(testSource, ifTrueSource); //判斷target是否為空 var testTarget = NotEqual(targetParameter, Constant(null, targetType)); var conditionTarget = IfThen(testTarget, conditionSource); var lambda = Lambda<Action<TSource, TTarget>>(conditionTarget, sourceParameter, targetParameter); return lambda.Compile(); } /// <summary> /// 類型是clas時賦值 /// </summary> /// <param name="sourceProperty"></param> /// <param name="targetProperty"></param> /// <param name="sourceType"></param> /// <param name="targetType"></param> /// <returns></returns> private static Expression GetClassExpression(Expression sourceProperty, Type sourceType, Type targetType) { //條件p.Item!=null var testItem = NotEqual(sourceProperty, Constant(null, sourceType)); //構造回調 ExprMapper<TSource, TTarget>.Map() var mapperType = typeof(ExprMapper<,>).MakeGenericType(sourceType, targetType); var iftrue = Call(mapperType.GetMethod(nameof(Map), new[] { sourceType }), sourceProperty); var conditionItem = Condition(testItem, iftrue, Constant(null, targetType)); return conditionItem; } /// <summary> /// 類型為集合時賦值 /// </summary> /// <param name="sourceProperty"></param> /// <param name="targetProperty"></param> /// <param name="sourceType"></param> /// <param name="targetType"></param> /// <returns></returns> private static Expression GetListExpression(Expression sourceProperty, Type sourceType, Type targetType) { //條件p.Item!=null var testItem = NotEqual(sourceProperty, Constant(null, sourceType)); //構造回調 ExprMapper<TSource, TTarget>.MapList() var sourceArg = sourceType.IsArray ? sourceType.GetElementType() : sourceType.GetGenericArguments()[0]; var targetArg = targetType.IsArray ? targetType.GetElementType() : targetType.GetGenericArguments()[0]; var mapperType = typeof(ExprMapper<,>).MakeGenericType(sourceArg, targetArg); var mapperExecMap = Call(mapperType.GetMethod(nameof(MapList), new[] { sourceType }), sourceProperty); Expression iftrue; if (targetType == mapperExecMap.Type) { iftrue = mapperExecMap; } else if (targetType.IsArray)//數組類型調用ToArray()方法 { iftrue = Call(mapperExecMap, mapperExecMap.Type.GetMethod("ToArray")); } else if (typeof(IDictionary<,>).IsAssignableFrom(targetType)) { iftrue = Constant(null, targetType);//字典類型不轉換 } else { iftrue = Convert(mapperExecMap, targetType); } var conditionItem = Condition(testItem, iftrue, Constant(null, targetType)); return conditionItem; } }
輸出的 表達式
格式化后
p => IIF((p != null), new TestB() { Id = p.Id, Name = p.Name, TestClass = IIF( (p.TestClass != null), Map(p.TestClass), null ), TestLists = IIF( (p.TestLists != null), MapList(p.TestLists).ToArray(), null ) }, null)
說明 Map(p.TestClass) MapList(p.TestLists).ToArray(), 完整的信息為 Mapper<TestC,TestD>.Map() Mapper<TestC,TestD>.MapList()
總結:解決嵌套類的核心代碼
101 //構造回調 Mapper<TSource, TTarget>.Map()
102 var mapperType = typeof(DataMapper<,>).MakeGenericType(sourceType, targetType);
103 var mapperExecMap = Expression.Call(mapperType.GetMethod(nameof(Map), new[] { sourceType }), sourceProperty);
利用Expression.Call 根據參數類型動態生成 對象映射的表達式
性能測試
寫了這么多最終目的還是為了解決性能問題,下面將對比下性能
1.測試類

using System.Diagnostics; using Nelibur.ObjectMapper; public static class MapperTest { public static AutoMapper.IMapper MapperObj = null; //執行次數 public static int Count = 100000; public static string StrTime = ""; //簡單類型 public static void Nomal() { Console.WriteLine($"******************簡單類型:{Count / 10000}萬次執行時間*****************"); var model = new TestA { Id = 1, Name = "張三", }; //計時 var sw = Stopwatch.StartNew(); for (int i = 0; i < Count; i++) { if (model != null) { var b = new TestB { Id = model.Id, Name = model.Name, }; } } sw.Stop(); Console.WriteLine($"原生的時間:{sw.ElapsedMilliseconds}ms"); StrTime += $",{sw.ElapsedMilliseconds}"; Exec(model); } //復雜類型 public static void Complex() { Console.WriteLine($"********************復雜類型:{Count / 10000}萬次執行時間*********************"); var model = new TestA { Id = 1, Name = "張三", TestClass = new TestC { Id = 2, Name = "lisi", }, }; //計時 var sw = Stopwatch.StartNew(); for (int i = 0; i < Count; i++) { if (model != null) { var b = new TestB { Id = model.Id, Name = model.Name, }; if (model.TestClass != null) { b.TestClass = new TestD { Id = i, Name = "lisi", }; } } } sw.Stop(); Console.WriteLine($"原生的時間:{sw.ElapsedMilliseconds}ms"); StrTime += $",{sw.ElapsedMilliseconds}"; Exec(model); } //嵌套類型 public static void Nest() { Console.WriteLine($"*****************嵌套類型:{Count / 10000}萬次執行時間*************************"); var model = new TestA { Id = 1, Name = "張三", TestClass = new TestC { Id = 1, Name = "lisi", SelfClass = new TestC { Id = 2, Name = "lisi", SelfClass = new TestC { Id = 3, Name = "lisi", SelfClass = new TestC { Id = 4, Name = "lisi", }, }, }, }, }; //計時 var item = model; var sw = Stopwatch.StartNew(); for (int i = 0; i < Count; i++) { //這里每一步需要做非空判斷的,書寫太麻煩省去了 if (model != null) { var b = new TestB { Id = model.Id, Name = model.Name, TestClass = new TestD { Id = model.TestClass.Id, Name = model.TestClass.Name, SelfClass = new TestD { Id = model.TestClass.SelfClass.Id, Name = model.TestClass.SelfClass.Name, SelfClass = new TestD { Id = model.TestClass.SelfClass.SelfClass.Id, Name = model.TestClass.SelfClass.SelfClass.Name, SelfClass = new TestD { Id = model.TestClass.SelfClass.SelfClass.SelfClass.Id, Name = model.TestClass.SelfClass.SelfClass.SelfClass.Name, }, }, }, }, }; } } sw.Stop(); Console.WriteLine($"原生的時間:{sw.ElapsedMilliseconds}ms"); StrTime += $",{sw.ElapsedMilliseconds}"; Exec(model); } //集合 public static void List() { Console.WriteLine($"********************集合類型:{Count / 10000}萬次執行時間***************************"); var model = new TestA { Id = 1, Name = "張三", TestLists = new List<TestC> { new TestC{ Id = 1, Name = "張三", }, new TestC{ Id = -1, Name = "張三", }, } }; //計時 var sw = Stopwatch.StartNew(); for (int i = 0; i < Count; i++) { var item = model; if (item != null) { var b = new TestB { Id = item.Id, Name = item.Name, TestLists = new List<TestD> { new TestD{ Id = item.Id, Name = item.Name, }, new TestD{ Id = -item.Id, Name = item.Name, }, }.ToArray() }; } } sw.Stop(); Console.WriteLine($"原生的時間:{sw.ElapsedMilliseconds}ms"); StrTime += $",{sw.ElapsedMilliseconds}"; Exec(model); } public static void Exec(TestA model) { //表達式 ExprMapper<TestA, TestB>.Map(model); var sw = Stopwatch.StartNew(); for (int i = 0; i < Count; i++) { var b = ExprMapper<TestA, TestB>.Map(model); } sw.Stop(); Console.WriteLine($"表達式的時間:{sw.ElapsedMilliseconds}ms"); StrTime += $",{sw.ElapsedMilliseconds}"; //AutoMapper sw.Restart(); for (int i = 0; i < Count; i++) { //var b = AutoMapper.Mapper.Map<TestA, TestB>(model); var bb = MapperObj.Map<TestB>(model); } sw.Stop(); Console.WriteLine($"AutoMapper時間:{sw.ElapsedMilliseconds}ms"); StrTime += $",{sw.ElapsedMilliseconds}"; //TinyMapper sw.Restart(); for (int i = 0; i < Count; i++) { var b = TinyMapper.Map<TestA, TestB>(model); } sw.Stop(); Console.WriteLine($"TinyMapper時間:{sw.ElapsedMilliseconds}ms"); StrTime += $",{sw.ElapsedMilliseconds}"; } }
2.調用測試

static void TestAllMapper() { string performanceFile = AppDomain.CurrentDomain.BaseDirectory + "perfReport.csv"; //AutoMapper.ExprMapper.Initialize(cfg => cfg.CreateMap<TestA, TestB>()); TestClass.MapperTest.MapperObj = new MapperConfiguration(cfg => { cfg.CreateMap<TestA, TestB>(); cfg.CreateMap<TestC, TestD>(); } ).CreateMapper(); TinyMapper.Bind<TestA, TestB>(); TestClass.ExprMapper<TestA, TestB>.Map(new TestA()); if (!File.Exists(performanceFile)) new Comm.FileHelper().WriteFile(performanceFile, $"執行次數,原生時間,表達式時間,AutoMapper時間,TinyMapper時間"); MapperTest.Count = 10000; MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-簡單"; MapperTest.Nomal(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-復雜"; MapperTest.Complex(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-嵌套"; MapperTest.Nest(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-集合"; MapperTest.List(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.Count = 100000; MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-簡單"; MapperTest.Nomal(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-復雜"; MapperTest.Complex(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-嵌套"; MapperTest.Nest(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-集合"; MapperTest.List(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.Count = 1000000; MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-簡單"; MapperTest.Nomal(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-復雜"; MapperTest.Complex(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-嵌套"; MapperTest.Nest(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-集合"; MapperTest.List(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.Count = 10000000; MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-簡單"; MapperTest.Nomal(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-復雜"; MapperTest.Complex(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-嵌套"; MapperTest.Nest(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); MapperTest.StrTime = $"{ MapperTest.Count / 10000}萬-集合"; MapperTest.List(); new Comm.FileHelper().WriteFile(performanceFile, MapperTest.StrTime, FileMode.Append); Console.WriteLine($"------------結束--------------------"); }
3.結果
1萬次
10萬次
100萬次
1000萬次
上圖結果AutoMapper 在非簡單類型的轉換上比其他方案有50倍以上的差距,幾乎就跟反射的結果一樣。
另外,可以查看運行目錄下的perfReport.csv文件,標記每次執行次數和時間。
作者:costyuan
GitHub地址:https://github.com/bieyuan/.net-core-DTO
出處:https://www.cnblogs.com/castyuan/p/9324088.html