不同类型不同字段如何实现深拷贝(可配置)

13 篇文章 0 订阅

不同类型不同字段如何实现深拷贝(可配置)

突发奇想,如果给我两个实体 A 与 B 他们的字段是这样的,实现将A对象的值赋值给B对象

   public class A
    {
      public string Name { set; get; }
      public int Age { set; get; }
      public DateTime BirthDate { set; get; }
      public string Email { set; get; }
    }
    public class B
    {
        public string PersonName { set; get; }
        public int PersonAge { set; get; }
        public DateTime PersonBirthDate { set; get; }
        public string PersonEmail { set; get; }
    }

最简单的通过 new 一个B 对象 将A里的值一个字段一个字段赋值给B,这种方式可以实现,但是写死的东西不灵活且如果字段超多怎么办?

如何才能将A类的Name字段赋给B类的PersonName字段同样达到可配置字段赋给任意字段的需求呢

通过最常用的反射去实现似乎有些麻烦,可以通过表达式树的方式实现:

    public static class DeepCopy
    {
        private static ConcurrentDictionary<string, object> actions = new ConcurrentDictionary<string, object>();

        /// <summary>
        /// 深拷贝
        /// </summary>
        /// <typeparam name="S">源类型</typeparam>
        /// <typeparam name="T">目标类型</typeparam>
        /// <param name="source">原对象</param>
        /// <param name="target">目标对象</param>
        /// <param name="dics">配置</param>
        public static void Copy<S, T>(S source, T target, Dictionary<string, string> dics = null)
        {
            string typeName = string.Format("{0}_{1}", typeof(S), typeof(T));
            if (!actions.TryGetValue(typeName, out object obj))
            {
                Action<S, T> executeAct = CreateCopier<S, T>(dics);
                actions.TryAdd(typeName, executeAct);
                obj = executeAct;
            }
            Action<S, T> action = (Action<S, T>)obj;
            action(source, target);
        }

        private static Action<S, T> CreateCopier<S, T>(Dictionary<string,string> dics)
        {
            var source = Expression.Parameter(typeof(S));
            var target = Expression.Parameter(typeof(T));
            BlockExpression block = null;
            if (dics == null)//如果未传入配置则按相同字段名进行赋值
            {
                var sourceParameter = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList();
                var targetParameter = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList();
                //根据相同的字段名
                var intersection = sourceParameter.Where(x => targetParameter.Where(y => y.Name == x.Name).Count() > 0);
                block = Expression.Block(intersection.Select(property => Expression.Assign(Expression.Property(target, property.Name), Expression.Property(source, property.Name))));
            }
            else
            {
                //根据配置来进行赋值 Key为B类中的字段 Value为A类中的字段
                block = Expression.Block(dics.Select(dic => Expression.Assign(Expression.Property(target, dic.Key), Expression.Property(source, dic.Value))));
            }
            return Expression.Lambda<Action<S, T>>(block, source, target).Compile();
        }
    }

例1 无配置传入根据相同字段进行赋值:

            A a = new A
            {
                Name = "小张",
                Age = 25,
                BirthDate = DateTime.Now,
                Email = "123@123.com"
            };
            
            //因为没有传入配置文件此处只赋值了相同字段 固只有Name具有值
            B b1 = new B();
            DeepCopy.Copy<A, B>(a,b1);
            Console.WriteLine(b1.Name);	

 

例2 有配置传入根据配置字段进行赋值:

            A a = new A
            {
                Name = "小张",
                Age = 25,
                BirthDate = DateTime.Now,
                Email = "123@123.com"
            };

            B b2 = new B();
            Dictionary<string, string> dic = new Dictionary<string, string>
            {
                { "Name", "Name" },
                { "PersonAge", "Age" },
                { "PersonBirthDate", "BirthDate" },
                { "PersonEmail", "Email" }
            };
            DeepCopy.Copy<A, B>(a, b2,dic);
            Console.WriteLine("姓名:"+b2.Name);
            Console.WriteLine("年龄:" + b2.PersonAge);
            Console.WriteLine("出生日期:" + b2.PersonBirthDate);
            Console.WriteLine("邮箱:" + b2.PersonEmail);

这里再说一下

 block = Expression.Block(dics.Select(dic => Expression.Assign(Expression.Property(target, dic.Key), Expression.Property(source, dic.Value))));

配置文件不光可以是键值对类型,只要是可以使用linq遍历并且可以取出值的都可以,最简单的例如配置文件存在数据库中取出来后通过select取出需要的值等等

本文主要研究在Copy对象的时候遇到字段不相同还需拷贝的情况

记录一下工作遇到的小问题。如有问题,还请大牛不吝赐教。

版权声明:本文为博主原创文章,未经博主允许不得转载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值