C# 对象类型映射转换方法总结,表达式树实现高效转换
对象类型映射转换常用方法
开发过程中经常会遇到对象类型之间的转换映射,例如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
关注我】。(●'◡'●)
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的【因为,我的写作热情也离不开您的肯定与支持,感谢您的阅读,我是【Jack_孟】!
本文来自博客园,作者:jack_Meng,转载请注明原文链接:https://www.cnblogs.com/mq0036/p/15034323.html
【免责声明】本文来自源于网络,如涉及版权或侵权问题,请及时联系我们,我们将第一时间删除或更改!
posted on 2021-07-20 12:48 jack_Meng 阅读(2939) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2018-07-20 Linux或树莓派3——挂载U盘、移动硬盘并设置rwx权限