ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C# 对象类型映射转换方法总结,表达式树实现高效转换

2021-07-20 13:01:44  阅读:315  来源: 互联网

标签:Name 映射 C# public 转换方法 PropertyType var Expression Id


       开发过程中经常会遇到对象类型之间的转换映射,例如Model和ViewModel之间的映射绑定,下面总结几种常见的转换方式。事先准备两个类:
CheckFileCheckFileModel


 	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));
 
END

 

出处:https://blog.csdn.net/qq_31176861/article/details/86551996

=======================================================================================

C# 高性能对象映射(表达式树实现)

前言

  上篇简单实现了对象映射,针对数组,集合,嵌套类并没有给出实现,这一篇继续完善细节。

 

开源对象映射类库映射分析

 1.AutoMapper 

   实现原理:主要通过表达式树Api 实现对象映射 

   优点: .net功能最全的对象映射类库。

   缺点:当出现复杂类型和嵌套类型时性能直线下降,甚至不如序列化快

 2.TinyMapper

   实现原理:主要通过Emit 实现对象映射 

   优点:速度非常快。在处理复杂类型和嵌套类型性能也很好

   缺点:相对AutoMapper功能上少一些,Emit的实现方案,在代码阅读和调试上相对比较麻烦,而表达式树直接观察 DebugView中生成的代码结构便可知道问题所在

 

 3. 本文的对象映射库

  针对AutoMapper 处理复杂类型和嵌套类型时性能非常差的情况,自己实现一个表达式树版的高性能方案

 

此篇记录下实现对象映射库的过程

  构造测试类

复制代码
 1     public class TestA
 2     {
 3         public int Id { get; set; }
 4         public string Name { get; set; }
 5 
 6         public TestC TestClass { get; set; }
 7 
 8         public IEnumerable<TestC> TestLists { get; set; }
 9     }
10 
11     public class TestB
12     {
13         public int Id { get; set; }
14         public string Name { get; set; }
15 
16         public TestD TestClass { get; set; }
17 
18         public TestD[] TestLists { get; set; }
19     }
20 
21     public class TestC
22     {
23         public int Id { get; set; }
24         public string Name { get; set; }
25 
26         public TestC SelfClass { get; set; }
27     }
28 
29     public class TestD
30     {
31         public int Id { get; set; }
32         public string Name { get; set; }
33 
34         public TestD SelfClass { get; set; }
35     }
复制代码

 

 1.初步实现

    利用表达式树给属性赋值 利用 Expresstion.New构造 var b=new B{};

复制代码
 1       private static Func<TSource, TTarget> GetMap<TSource, TTarget>()
 2         {
 3             var sourceType = typeof(TSource);
 4             var targetType = typeof(TTarget);
 5 
 6             //构造 p=>
 7             var parameterExpression = Expression.Parameter(sourceType, "p");
 8 
 9             //构造 p=>new TTarget{ Id=p.Id,Name=p.Name };
10             var memberBindingList = new List<MemberBinding>();
11             foreach (var sourceItem in sourceType.GetProperties())
12             {
13                 var targetItem = targetType.GetProperty(sourceItem.Name);
14                 if (targetItem == null || sourceItem.PropertyType != targetItem.PropertyType)
15                     continue;
16 
17                 var property = Expression.Property(parameterExpression, sourceItem);
18                 var memberBinding = Expression.Bind(targetItem, property);
19                 memberBindingList.Add(memberBinding);
20             }
21             var memberInitExpression = Expression.MemberInit(Expression.New(targetType), memberBindingList);
22 
23             var lambda = Expression.Lambda<Func<TSource, TTarget>>(memberInitExpression, parameterExpression );
24 
25             Console.WriteLine(lambda);
26             return lambda.Compile();
27         }
复制代码

 

  调用如下

复制代码
14 
15     class Program
16     {
17         static void Main(string[] args)
18         {
19             var testA = new TestA { Id = 1, Name = "张三" };
20             var func = Map<TestA, TestB>();
21             TestB testB = func(testA);
22             Console.WriteLine($"testB.Id={testB.Id},testB.Name={testB.Name}");
23             Console.ReadLine();
24         }
25     }
复制代码

  输出结果

 

总结:此方法需要调用前需要手动编译下,然后再调用委托没有缓存委托,相对麻烦。

2.缓存实现

   利用静态泛型类缓存泛型委托

复制代码
 1     public class DataMapper<TSource, TTarget>
 2     {
 3         private static Func<TSource, TTarget> MapFunc { get; set; }
 4 
 5         public static TTarget Map(TSource source)
 6         {
 7             if (MapFunc == null)
 8                 MapFunc = GetMap();//方法在上边
 9             return MapFunc(source);
10         }11    }
复制代码

   调用方法

复制代码
1         static void Main(string[] args)
2         {
3             var testA = new TestA { Id = 1, Name = "张三" };
4             TestB testB = DataMapper<TestA, TestB>.Map(testA);//委托不存在时自动生成,存在时调用静态缓存
5 
6             Console.WriteLine($"testB.Id={testB.Id},testB.Name={testB.Name}");
7             Console.ReadLine();
8         }
复制代码

   输出结果

 

  总结:引入静态泛型类能解决泛型委托缓存提高性能,但是有两个问题  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         }
复制代码

  将上面的代码翻译成表达式树

复制代码
 1         private static Func<TSource, TTarget> GetMap()
 2         {
 3             var sourceType = typeof(TSource);
 4             var targetType = typeof(TTarget);
 5 
 6             //Func委托传入变量
 7             var parameter = Expression.Parameter(sourceType);
 8 
 9             //声明一个返回值变量
10             var variable = Expression.Variable(targetType);
11             //创建一个if条件表达式
12             var test = Expression.NotEqual(parameter, Expression.Constant(null, sourceType));// p==null;
13             var ifTrue = Expression.Block(GetExpression(parameter, variable, sourceType, targetType));
14             var IfThen = Expression.IfThen(test, ifTrue);
15 
16             //构造代码块 
17             var block = Expression.Block(new[] { variable }, parameter, IfThen, variable);
18 
19             var lambda = Expression.Lambda<Func<TSource, TTarget>>(block, parameter);
20             return lambda.Compile();
21         }
22 
23         private static List<Expression> GetExpression(Expression parameter, Expression variable, Type sourceType, Type targetType)
24         {
25             //创建一个表达式集合
26             var expressions = new List<Expression>();
27 
28             expressions.Add(Expression.Assign(variable, Expression.MemberInit(Expression.New(targetType))));
29 
30             foreach (var targetItem in targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite))
31             {
32                 var sourceItem = sourceType.GetProperty(targetItem.Name);
33 
34                 //判断实体的读写权限
35                 if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic)
36                     continue;
37 
38                 var sourceProperty = Expression.Property(parameter, sourceItem);
39                 var targetProperty = Expression.Property(variable, targetItem);
40 
41                 //判断都是class 且类型不相同时
42                 if (targetItem.PropertyType.IsClass && sourceItem.PropertyType.IsClass && targetItem.PropertyType != sourceItem.PropertyType)
43                 {
44                     if (targetItem.PropertyType != targetType)//不处理嵌套循环的情况
45                     {
46                         //由于类型是class 所以默认值是null
47                         var testItem = Expression.NotEqual(sourceProperty, Expression.Constant(null, sourceItem.PropertyType));
48 
49                         var itemExpressions = GetExpression(sourceProperty, targetProperty, sourceItem.PropertyType, targetItem.PropertyType);
50                         var ifTrueItem = Expression.Block(itemExpressions);
51 
52                         var IfThenItem = Expression.IfThen(testItem, ifTrueItem);
53                         expressions.Add(IfThenItem);
54 
55                         continue;
56                     }
57                 }
58 
59                 //目标值类型时 且两者类型不一致时跳过
60                 if (targetItem.PropertyType != sourceItem.PropertyType)
61                     continue;
62 
63                 expressions.Add(Expression.Assign(targetProperty, sourceProperty));
64             }
65 
66             return expressions;
67         }
复制代码

总结:此方案,运用 Expression.IfThen(testItem, ifTrueItem) 判断空值问题,通过递归调用 GetExpression()方法,处理复杂类型。 但是。。。针对嵌套类仍然不能解决。因为表达式树是在实际调用方法之前就生成的,在没有实际的

           参数值传入之前,生成的表达式是不知道有多少层级的。有个比较low的方案是,预先设定嵌套层级为10层,然后生成一个有10层 if(P!=null) 的判断。如果传入的参数层级超过10层了呢,就得手动调整生成的树,此方案也否决。

           最后得出的结论只能在表达式中动态调用方法。

4.最终版本

   通过动态调用方法解决嵌套类,代码如下

复制代码
  using static System.Linq.Expressions.Expression;
    public static class Mapper<TSource, TTarget> where TSource : class where TTarget : class
    {
public readonly static Func<TSource, TTarget> MapFunc = GetMapFunc();

public 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);

public static List<TTarget> MapList(IEnumerable<TSource> sources)=> sources.Select(MapFunc).ToList();



/// <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 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();
        }

        /// <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));

            //构造回调 Mapper<TSource, TTarget>.Map()
            var mapperType = typeof(Mapper<,>).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));

            //构造回调 Mapper<TSource, TTarget>.MapList()
            var sourceArg = sourceType.IsArray ? sourceType.GetElementType() : sourceType.GetGenericArguments()[0];
            var targetArg = targetType.IsArray ? targetType.GetElementType() : targetType.GetGenericArguments()[0];
            var mapperType = typeof(Mapper<,>).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;
        }

        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();
        }
    }
复制代码

 

 

 

 

输出的 表达式

格式化后

复制代码
 1 p => IIF((p != null), 
 2      new TestB() 
 3      {
 4        Id = p.Id, 
 5        Name = p.Name, 
 6        TestClass = IIF(
 7                    (p.TestClass != null),
 8                     Map(p.TestClass),
 9                     null
10                     ),
11        TestLists = IIF(
12                      (p.TestLists != null),
13                       MapList(p.TestLists).ToArray(),
14                       null
15                      )
16        },
17        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.测试类

复制代码
  1     public static class MapperTest
  2     {
  3         //执行次数
  4         public static int Count = 100000;
  5 
  6         //简单类型
  7         public static void Nomal()
  8         {
  9             Console.WriteLine($"******************简单类型:{Count / 10000}万次执行时间*****************");
 10             var model = new TestA
 11             {
 12                 Id =1,
 13                 Name = "张三",
 14             };
 15 
 16             //计时
 17             var sw = Stopwatch.StartNew();
 18             for (int i = 0; i < Count; i++)
 19             {
 20                 if (model != null)
 21                 {
 22                     var b = new TestB
 23                     {
 24                         Id = model.Id,
 25                         Name = model.Name,
 26                     };
 27                 }
 28             }
 29             sw.Stop();
 30             Console.WriteLine($"原生的时间:{sw.ElapsedMilliseconds}ms");
 31 
 32             Exec(model);
 33         }
 34 
 35         //复杂类型
 36         public static void Complex()
 37         {
 38             Console.WriteLine($"********************复杂类型:{Count / 10000}万次执行时间*********************");
 39             var model = new TestA
 40             {
 41                 Id = 1,
 42                 Name = "张三",
 43                 TestClass = new TestC
 44                 {
 45                     Id = 2,
 46                     Name = "lisi",
 47                 },
 48             };
 49 
 50             //计时
 51             var sw = Stopwatch.StartNew();
 52             for (int i = 0; i < Count; i++)
 53             {
 54 
 55                 if (model != null)
 56                 {
 57                     var b = new TestB
 58                     {
 59                         Id = model.Id,
 60                         Name = model.Name,
 61                     };
 62                     if (model.TestClass != null)
 63                     {
 64                         b.TestClass = new TestD
 65                         {
 66                             Id = i,
 67                             Name = "lisi",
 68                         };
 69                     }
 70                 }
 71             }
 72             sw.Stop();
 73             Console.WriteLine($"原生的时间:{sw.ElapsedMilliseconds}ms");
 74             Exec(model);
 75         }
 76 
 77         //嵌套类型
 78         public static void Nest()
 79         {
 80             Console.WriteLine($"*****************嵌套类型:{Count / 10000}万次执行时间*************************");
 81             var model = new TestA
 82             {
 83                 Id = 1,
 84                 Name = "张三",
 85                 TestClass = new TestC
 86                 {
 87                     Id = 1,
 88                     Name = "lisi",
 89                     SelfClass = new TestC
 90                     {
 91                         Id = 2,
 92                         Name = "lisi",
 93                         SelfClass = new TestC
 94                         {
 95                             Id = 3,
 96                             Name = "lisi",
 97                             SelfClass = new TestC
 98                             {
 99                                 Id = 4,
100                                 Name = "lisi",
101                             },
102                         },
103                     },
104                 },
105             };
106             //计时
107             var item = model;
108             var sw = Stopwatch.StartNew();
109             for (int i = 0; i < Count; i++)
110             {
111                 //这里每一步需要做非空判断的,书写太麻烦省去了
112                 if (model != null)
113                 {
114                     var b = new TestB
115                     {
116                         Id = model.Id,
117                         Name = model.Name,
118                         TestClass = new TestD
119                         {
120                             Id = model.TestClass.Id,
121                             Name = model.TestClass.Name,
122                             SelfClass = new TestD
123                             {
124                                 Id = model.TestClass.SelfClass.Id,
125                                 Name = model.TestClass.SelfClass.Name,
126                                 SelfClass = new TestD
127                                 {
128                                     Id = model.TestClass.SelfClass.SelfClass.Id,
129                                     Name = model.TestClass.SelfClass.SelfClass.Name,
130                                     SelfClass = new TestD
131                                     {
132                                         Id = model.TestClass.SelfClass.SelfClass.SelfClass.Id,
133                                         Name = model.TestClass.SelfClass.SelfClass.SelfClass.Name,
134                                     },
135                                 },
136                             },
137                         },
138                     };
139                 }
140             }
141             sw.Stop();
142             Console.WriteLine($"原生的时间:{sw.ElapsedMilliseconds}ms");
143 
144             Exec(model);
145         }
146 
147         //集合
148         public static void List()
149         {
150             Console.WriteLine($"********************集合类型:{Count/10000}万次执行时间***************************");
151 
152             var model = new TestA
153             {
154                 Id = 1,
155                 Name = "张三",
156                 TestLists = new List<TestC> {
157                             new TestC{
158                              Id = 1,
159                             Name =  "张三",
160                            },
161                             new TestC{
162                             Id = -1,
163                             Name =  "张三",
164                            },
165                         }
166             };
167 
168 
169             //计时
170             var sw = Stopwatch.StartNew();
171             for (int i = 0; i < Count; i++)
172             {
173                 var item = model;
174                 if (item != null)
175                 {
176                     var b = new TestB
177                     {
178                         Id = item.Id,
179                         Name = item.Name,
180                         TestLists = new List<TestD> {
181                             new TestD{
182                                    Id = item.Id,
183                             Name = item.Name,
184                            },
185                             new TestD{
186                             Id = -item.Id,
187                             Name = item.Name,
188                            },
189                         }.ToArray()
190                     };
191                 }
192             }
193             sw.Stop();
194             Console.WriteLine($"原生的时间:{sw.ElapsedMilliseconds}ms");
195 
196             Exec(model);
197         }
198 
199         public static void Exec(TestA model)
200         {
201             //表达式
202             Mapper<TestA, TestB>.Map(model);
203             var sw = Stopwatch.StartNew();
204             for (int i = 0; i < Count; i++)
205             {
206                 var b = Mapper<TestA, TestB>.Map(model);
207             }
208             sw.Stop();
209             Console.WriteLine($"表达式的时间:{sw.ElapsedMilliseconds}ms");
210 
211             //AutoMapper
212             sw.Restart();
213             for (int i = 0; i < Count; i++)
214             {
215                 var b = AutoMapper.Mapper.Map<TestA, TestB>(model);
216             }
217             sw.Stop();
218             Console.WriteLine($"AutoMapper时间:{sw.ElapsedMilliseconds}ms");
219 
220             //TinyMapper
221             sw.Restart();
222             for (int i = 0; i < Count; i++)
223             {
224                 var b = TinyMapper.Map<TestA, TestB>(model);
225             }
226             sw.Stop();
227             Console.WriteLine($"TinyMapper时间:{sw.ElapsedMilliseconds}ms");
228         }
229     }
230 
231 
复制代码

2.调用测试

复制代码
 1         static void Main(string[] args)
 2         {
 3             AutoMapper.Mapper.Initialize(cfg => cfg.CreateMap<TestA, TestB>());
 4             TinyMapper.Bind<TestA, TestB>();
 5             Mapper<TestA, TestB>.Map(new TestA());
 6 
 7 
 8             MapperTest.Count = 10000;
 9             MapperTest.Nomal();
10             MapperTest.Complex();
11             MapperTest.Nest();
12             MapperTest.List();
13 
14             MapperTest.Count = 100000;
15             MapperTest.Nomal();
16             MapperTest.Complex();
17             MapperTest.Nest();
18             MapperTest.List();
19 
20             MapperTest.Count = 1000000;
21             MapperTest.Nomal();
22             MapperTest.Complex();
23             MapperTest.Nest();
24             MapperTest.List();
25 
26             MapperTest.Count = 10000000;
27             MapperTest.Nomal();
28             MapperTest.Complex();
29             MapperTest.Nest();
30             MapperTest.List();
31 
32 
33             Console.WriteLine($"------------结束--------------------");
34             Console.ReadLine();
35         }
复制代码

3.结果

1万次

 

10万次

 

100万次

 

1000万次

 

上图结果AutoMapper 在非简单类型的转换上比其他方案有50倍以上的差距,几乎就跟反射的结果一样。

 

作者:costyuan

GitHub地址:https://github.com/bieyuan/.net-core-DTO

 

 

出处:https://www.cnblogs.com/castyuan/p/9324088.html

标签:Name,映射,C#,public,转换方法,PropertyType,var,Expression,Id
来源: https://www.cnblogs.com/mq0036/p/15034323.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有