.Net 对象与对象之间的映射转换的6中方式以及性能对比

我们在.Net开发的过程中,经常会遇到一个实体对象与另一个实体对象之间的映射转换,接下来我们将依次去实现6个对象间映射转换的方式,并对他们进行性能测试,找出其中效率最高的方式。

  1. 通过对象Copy,通过new一个新的实体对象通过手动赋值的方式实现

    public class ObjectCopyMapper
    {
        /// <summary>
        /// 第一种就是通过New一个目标对象给所有属性手动赋值
        /// </summary>
        public static UserModel Trans(User userEntry)
        {
            return new UserModel()
            {
                Id = userEntry.Id,
                UserName = userEntry.UserName,
                NickName = userEntry.NickName,
            };
        }
    }

  2. 通过反射依次找出目标实体对象中需要的属性和字段,再从源实体对象中获取到对应属性和字段的值

     public class ReflectionMapper
     {
         /// <summary>
         /// 通过反射的方式实现对象之间的映射
         /// </summary>
         /// <typeparam name="TIn"></typeparam>
         /// <typeparam name="TOut"></typeparam>
         /// <param name="tIn"></param>
         /// <returns></returns>
         public static TOut Trans<TIn, TOut>(TIn tIn)
         {
             TOut tOut = Activator.CreateInstance<TOut>();
             foreach (var itemOut in tOut.GetType().GetProperties())
             {
                 var propIn = tIn.GetType().GetProperty(itemOut.Name);
                 itemOut.SetValue(tOut, propIn.GetValue(tIn));
             }
    
             foreach (var itemOut in tOut.GetType().GetFields())
             {
                 var fieldIn = tIn.GetType().GetField(itemOut.Name);
                 itemOut.SetValue(tOut, fieldIn.GetValue(tIn));
             }
    
             return tOut;
         }
     }

  3. 通过Json序列化反序列化的方式实现,这种是最简单的

    public class SerializeMapper
    {
        /// <summary>
        /// 通过json序列化反序列化的方式实现
        /// </summary>
        /// <typeparam name="TIn"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="tIn"></param>
        /// <returns></returns>
        public static TOut Trans<TIn, TOut>(TIn tIn)
        {
            return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(tIn));
        }
    }

  4. 通过表达式目录树解析并缓存在字典中

     public class ExpressionMapper
     {
         private static Dictionary<string, object> dics = new Dictionary<string, object>();
    
         /// <summary>
         /// 采用字典缓存表达式树实现
         /// </summary>
         /// <typeparam name="TIn"></typeparam>
         /// <typeparam name="TOut"></typeparam>
         /// <param name="tIn"></param>
         /// <returns></returns>
         public static TOut Trans<TIn, TOut>(TIn tIn)
         {
             string key = $"funckey_{typeof(TIn).FullName}_{typeof(TOut).FullName}";
             if (!dics.ContainsKey(key))
             {
                 ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
                 List<MemberBinding> memberBindings = new List<MemberBinding>();
                 foreach(var item in typeof(TOut).GetProperties())
                 {
                     MemberExpression memberExpression = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
                     MemberBinding memberBinding = Expression.Bind(item, memberExpression);
                     memberBindings.Add(memberBinding);
                 }
                 foreach (var item in typeof(TOut).GetFields())
                 {
                     MemberExpression memberExpression = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
                     MemberBinding memberBinding = Expression.Bind(item, memberExpression);
                     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<TIn, TOut> func = lambda.Compile();//只拼装一次
                 dics[key] = func;
             }
    
             return ((Func<TIn, TOut>)dics[key]).Invoke(tIn);
         }
     }

  5. 通过表示是目录树解析并应用泛型缓存

     /// <summary>
     /// 采用泛型缓存表达式目录树实现
     /// </summary>
     /// <typeparam name="TIn"></typeparam>
     /// <typeparam name="TOut"></typeparam>
     public class ExpressionGenericMapper<TIn, TOut>
     {
         private static Func<TIn, TOut> _FUNC = null;
         static ExpressionGenericMapper()
         {
             ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
             List<MemberBinding> memberBindingList = 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);
                 memberBindingList.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);
                 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 = lambda.Compile();//拼装是一次性的
         }
         public static TOut Trans(TIn t)
         {
             return _FUNC(t);
         }
     }

  6. AutoMapper%E5%BA%93%E5%AE%9E%E7%8E%B0">通过AutoMapper库实现

    var configuration = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<User, UserModel>();
    });
    configuration.AssertConfigurationIsValid();
    var mapper = configuration.CreateMapper();
    UserModel dest = mapper.Map<UserModel>(user);

  7. 性能测试,我们进行100W次映射转换,统计各种方式需要的执行时间

    User user = new User() { 
        Id = 1,
        UserName = "Test",
        NickName = "测试用户",
    };
    
    long commonTime = 0;
    Stopwatch watch = new Stopwatch();
    watch.Start();
    {
        
        for (int i = 0; i < 1_000_000; i++)
        {
            ObjectCopyMapper.Trans(user);
        }
        watch.Stop();
        commonTime = watch.ElapsedMilliseconds;
        Console.WriteLine($"ObjectCopyMapper 用时:{commonTime}ms");
    }
    
    {
        watch.Restart();
        for (int i = 0; i < 1_000_000; i++)
        {
            ReflectionMapper.Trans<User, UserModel>(user);
        }
        watch.Stop();
        commonTime = watch.ElapsedMilliseconds;
        Console.WriteLine($"ReflectionMapper 用时:{commonTime}ms");
    }
    
    {
        watch.Restart();
        for (int i = 0; i < 1_000_000; i++)
        {
            SerializeMapper.Trans<User, UserModel>(user);
        }
        watch.Stop();
        commonTime = watch.ElapsedMilliseconds;
        Console.WriteLine($"SerializeMapper 用时:{commonTime}ms");
    }
    
    {
        watch.Restart();
        for (int i = 0; i < 1_000_000; i++)
        {
            ExpressionMapper.Trans<User, UserModel>(user);
        }
        watch.Stop();
        commonTime = watch.ElapsedMilliseconds;
        Console.WriteLine($"ExpressionMapper 用时:{commonTime}ms");
    }
    
    {
        watch.Restart();
        for (int i = 0; i < 1_000_000; i++)
        {
           UserModel model = ExpressionGenericMapper<User, UserModel>.Trans(user);
        }
        watch.Stop();
        commonTime = watch.ElapsedMilliseconds;
        Console.WriteLine($"ExpressionGenericMapper 用时:{commonTime}ms");
    }
    
    {
        watch.Restart();
        var configuration = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<User, UserModel>();
        });
        configuration.AssertConfigurationIsValid();
        var mapper = configuration.CreateMapper();
        for (int i = 0; i < 1_000_000; i++)
        {
            UserModel dest = mapper.Map<UserModel>(user);
        }
        watch.Stop();
        commonTime = watch.ElapsedMilliseconds;
        Console.WriteLine($"AutoMapper 用时:{commonTime}ms");
    }

    测试结果:

        ObjectCopyMapper 用时:52ms
        ReflectionMapper 用时:892ms
        SerializeMapper 用时:3468ms
        ExpressionMapper 用时:203ms
        ExpressionGenericMapper 用时:35ms
        AutoMapper 用时:209ms

        根据上面的测试数据,可以发现用时最长的是我们Json序列化和反序列化,用时最短的是我们自己封装的表达式目录树用泛型缓存。


http://www.niftyadmin.cn/n/5457093.html

相关文章

Vite 为什么比 Webpack 快?

目录 1. Webpack 的构建原理 2. Script 的模块化&#xff08;主流浏览器对 ES Modules 的支持&#xff09; 3. Webpack vs Vite 开发模式的差异 对 ES Modules 的支持 底层语言的差异 热更新的处理 1. Webpack 的构建原理 前端之所以需要类似于 Webpack 这样的构建工具&…

天文算法--复活节日期

复活节日期的计算 package cn.ancony.chinese_calendar;/*** 计算指定年基督教复活节星期日&#xff08;Easter Sunday&#xff09;日期的方法*/ public class EasterSunday {/*** 复活节星期日的推算规则:春分后&#xff08;含春分&#xff09;的第一次满月后的第一个星期日。…

【二叉树】Leetcode 230. 二叉搜索树中第K小的元素【中等】

二叉搜索树中第K小的元素 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 示例1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#xff1a;1 解…

智慧公厕,为智慧城市建设注入了新的活力

随着智慧城市的快速发展&#xff0c;公共厕所不再是简单的功能设施&#xff0c;而是成为了提升城市形象、改善民生服务的重要一环。智慧公厕作为新形态的公共厕所&#xff0c;通过精准监测公厕内部的人体活动状态、人体存在状态、空气质量情况、环境变化情况、设施设备运行状态…

Long long类型比较大小

long 与 Long long类型和Long类型是不一样&#xff0c;long类型属于基本的数据类型&#xff0c;而Long是long类型的包装类。 结论 long是基本数据类型&#xff0c;判断是否相等时使用 &#xff0c;即可判断值是否相等。&#xff08;基本数据类型没有equals()方法&#xff0…

图论之路径条数专题

一直忙着金工实习蓝桥杯&#xff0c;好久没有看图论了&#xff0c;今天就小试几题享受下被虐的快感。 1.最短路拓扑 首先来几个结论&#xff1a; 1.最短路图没有环&#xff08;可以用反证法证明&#xff09; 2.dis[u]edge[u,v]dis[v]&#xff0c;那么u,v端点的边一定在最短路…

FPGA 图像边缘检测(Canny算子)

1 顶层代码 timescale 1ns / 1ps //边缘检测二阶微分算子&#xff1a;canny算子module image_canny_edge_detect (input clk,input reset, //复位高电平有效input [10:0] img_width,input [ 9:0] img_height,input [ 7:0] low_threshold,input [ 7:0] high_threshold,input va…

C/C++ ③ —— C++11新特性

1. 类型推导 1.1 auto auto可以让编译器在编译期就推导出变量的类型 auto的使⽤必须⻢上初始化&#xff0c;否则⽆法推导出类型auto在⼀⾏定义多个变量时&#xff0c;各个变量的推导不能产⽣⼆义性&#xff0c;否则编译失败auto不能⽤作函数参数在类中auto不能⽤作⾮静态成员…