C# 急速深度拷贝

怎样深度拷贝最快呢?

假设有这样的俩个对象

    public class People
    {
        public int Age { get; set; }
        public string Name { get; set; }
        public string Sex { get; set; }
    }
    public class Man
    {
        public int ID { get;private set; }
        public int Age { get; set; }
        public string Name { get; set; }
        public string Sex { get; set; }
    }

如果我们将People的所有属性都赋值给Man。如果数量不多,我们一个个手写就好了,但是假设对象属性有成百上千个怎么办???一个个手写?那还不累死!

一般情况下有俩种办法

第一种,反射,逐个复制,但是我们都知道,反射是很慢的。性能并不好;

第二种,通过JsonConverter.序列化再反序列化,(⊙o⊙)…。性能比反射还差。

那有什么更好的办法吗???

第一种 通过字节缓冲。将成员信息缓冲到字典里面去,读取的时候再去里面查找。但是我们知道,字典是比较耗费时间的。

第二种 泛型缓冲,啥叫泛型缓冲?其实在C#里面 例如类 List<People> 在编译之后和 List<Man> 还有关系吗? 其实编译器是将其编译成为了俩单独的类。如果我们将我们的数据 存在类里面那么性能便是最高最快的。

废话说多了,贴代码。

public class DictionaryExpressionMapper
    {
        private static Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
        public static TOut Copy<Tin, TOut>(Tin tin)
        {
            var key = $"funckey_{typeof(Tin).FullName}_{typeof(TOut).FullName}";
            if (!keyValuePairs.ContainsKey(key))
            {
                var parameter = Expression.Parameter(typeof(Tin), "p");
                List<MemberBinding> memberBindings = new List<MemberBinding>();
                Copy<Tin>(typeof(TOut).GetProperties(), memberBindings, parameter);
                var init = Expression.MemberInit(Expression.New(typeof(TOut)),memberBindings.ToArray());
                var lambda = Expression.Lambda<Func<Tin, TOut>>(init, parameter);
                keyValuePairs[key] = lambda.Compile();
            }
            return ((Func<Tin, TOut>)keyValuePairs[key]).Invoke(tin);
        }
        /// <summary>
        /// 复制信息
        /// </summary>
        /// <typeparam name="Tin"></typeparam>
        /// <param name="memberInfos">对象成员列表</param>
        /// <param name="memberBindings">成员绑定表</param>
        /// <param name="parameter">表达式参数</param>
        private static void Copy<Tin>(IEnumerable<MemberInfo> memberInfos, List<MemberBinding> memberBindings, ParameterExpression parameter)
        {
            var propertys = typeof(Tin);
            foreach (var item in memberInfos)
            {
                if (item is PropertyInfo property1 && property1.CanWrite == false)
                {
                    continue;
                }
                var gg = propertys.GetProperty(item.Name);
                if (gg == null)
                {
                    continue;
                }
                var property = Expression.Property(parameter, gg);
                var member = Expression.Bind(item, property);
                memberBindings.Add(member);

            }
        }
    }

上面的代码是干啥的?核心就是动态生成LINQ语句。逐个将属性读取出来,然后逐个编写LINQ 属性赋值语句,最后新建初始化TOUT。

我们知道 在字典里查找数据时其实是通过传入KEY的哈希值查找的,速度还是比较慢的。我们再次优化

    public static class GenericExpressionMapper<Tin,TOut>
    {
        private static Func<Tin, TOut> func = null;
        static  GenericExpressionMapper()
        {
            var parameter = Expression.Parameter(typeof(Tin), "p");
            List<MemberBinding> memberBindings = new List<MemberBinding>();
            Copy(typeof(TOut).GetProperties(), memberBindings, parameter);
            var init = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindings.ToArray());
            var lambda = Expression.Lambda<Func<Tin, TOut>>(init, parameter);
            func = lambda.Compile();
        }
        /// <summary>
        /// 深度复制
        /// </summary>
        /// <param name="tin"></param>
        /// <returns></returns>
        public static TOut Copy(Tin tin) => func.Invoke(tin);
        /// <summary>
        /// 复制信息
        /// </summary>
        /// <typeparam name="Tin"></typeparam>
        /// <param name="memberInfos">对象成员列表</param>
        /// <param name="memberBindings">成员绑定表</param>
        /// <param name="parameter">表达式参数</param>
        private static void Copy(IEnumerable<MemberInfo> memberInfos, List<MemberBinding> memberBindings, ParameterExpression parameter)
        {
            var propertys = typeof(Tin);
            foreach (var item in memberInfos)
            {
                if (item is PropertyInfo property1 && property1.CanWrite == false)
                {
                    continue;
                }
                var gg = propertys.GetProperty(item.Name);
                if (gg == null)
                {
                    continue;
                }
                var property = Expression.Property(parameter, gg);
                var member = Expression.Bind(item, property);
                memberBindings.Add(member);

            }
        }
    }

升级后一对TIN和TOUT对应一个新的类。如此相当于我们直接操作类了,性能杠杠的。

调用就简单了。非常简单。如下 

            var peop = new People() {Age=123,Name="李四",Sex="女性" };
            var res = GenericExpressionMapper<People, Man>.Copy(peop);
            var re2 = GenericExpressionMapper<People, Man>.Copy(peop);

第一次调用泛型缓冲时,我们会调用其静态构造器,并生成其静态赋值委托。第二次调用时由于 静态构造器只调用一次,我们就直接调用其泛型委托了,啊哈,一下就赋值完成。

  经过100万次赋值测试,泛型委托深拷贝方式 是 原生硬编码的俩倍。快不快?字典形式深拷贝是原生硬码方式的10倍。反射和序列化方式是原生硬编码的100多倍。

最后我们将上面的代码优化掉,一个扩展方法使用起来可就美滋滋了

    /// <summary>
    /// 克隆扩展
    /// </summary>
    public static class CloneExtension

    {

        private static Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
        /// <summary>
        /// 克隆复制当前对象
        /// </summary>
        /// <typeparam name="TIn"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public static TIn Clone<TIn>(this TIn data) => data.Clone<TIn, TIn>();
        /// <summary>
        /// 克隆服务对象
        /// </summary>
        /// <typeparam name="Tin"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="tin"></param>
        /// <returns></returns>
        public static TOut Clone<Tin, TOut>(this Tin tin)

        {

            var key = $"funckey_{typeof(Tin).FullName}_{typeof(TOut).FullName}";

            if (!keyValuePairs.ContainsKey(key))

            {

                var parameter = Expression.Parameter(typeof(Tin), "p");

                List<MemberBinding> memberBindings = new List<MemberBinding>();

                Copy<Tin>(typeof(TOut).GetProperties(), memberBindings, parameter);

                var init = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindings.ToArray());

                var lambda = Expression.Lambda<Func<Tin, TOut>>(init, parameter);

                keyValuePairs[key] = lambda.Compile();

            }

            return ((Func<Tin, TOut>)keyValuePairs[key]).Invoke(tin);

        }

        /// <summary>
        /// 复制信息
        /// </summary>
        /// <typeparam name="Tin"></typeparam>
        /// <param name="memberInfos">对象成员列表</param>
        /// <param name="memberBindings">成员绑定表</param>
        /// <param name="parameter">表达式参数</param>
        private static void Copy<Tin>(IEnumerable<MemberInfo> memberInfos, List<MemberBinding> memberBindings, ParameterExpression parameter)

        {

            var propertys = typeof(Tin);

            foreach (var item in memberInfos)

            {

                if (item is PropertyInfo property1 && property1.CanWrite == false)

                {

                    continue;

                }

                var gg = propertys.GetProperty(item.Name);

                if (gg == null)

                {

                    continue;

                }

                var property = Expression.Property(parameter, gg);

                var member = Expression.Bind(item, property);

                memberBindings.Add(member);



            }

        }

    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值