c#基础工具—对象克隆ObjectCloneUtils

一、情景分析

   对象克隆对程序开发来说是一个很常见的小需求,就c#而言实现也很简单,浅克隆的的话使用MemberwiseClone,深克隆的话使用一种序列化方式,然后在反序列化就可以了。这篇文章提供的代码,不涉及到高深的技术,如果有什么亮点的话,可能就是通用性强点。

二、代码设计要求

  1、要实现两个方法:浅克隆Clone和深DeepClone

  2、要做到比较通用,支持泛型。使用方便,一遍需求不用增加代码

  3、根据约定优于配置的原则,在设计中减少接口和配置文件的约束。但要能灵活的控制克隆过程,尤其是深克隆

三、代码实现

 /// 
    /// 对象克隆工具
    /// 
    public static class ObjectCloneUtils
    {
        static ObjectCloneUtils()
        {
            InitMemberwiseClone();
        }
        #region 克隆缓存
        /// 
        /// 浅克隆
        /// 
        /// 
        /// 
        /// 
        public static T Clone(T obj)
        {
            return (T)m_MemberwiseClone?.Invoke(obj);
        }
        private static Func m_MemberwiseClone;
        /// 
        /// 初始化浅克隆方法
        /// 
        private static void InitMemberwiseClone()
        {
            var type = typeof(Object);
            var memberwiseClone = type.GetMethod("MemberwiseClone",
           BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            var source = Expression.Parameter(typeof(Object), "p");
            var body_objToType = Expression.Convert(source, memberwiseClone.DeclaringType);
            MethodCallExpression cloneMethod = Expression.Call(body_objToType, memberwiseClone);
            var useValue = Expression.Convert(cloneMethod, typeof(object));
            m_MemberwiseClone= Expression.Lambda>(useValue, source).Compile();
        }



        #endregion

        #region 深克隆
        /// 
        /// 深克隆
        /// 
        /// 
        /// 
        /// 
        /// 
        public static T DeepClone(T obj, bool useDefaultClone = true)
        {
            var method = GetDeepCloneMethod(obj.GetType());
            if (method != null)
            {
                return (T)method?.Invoke(obj);
            }
            else if (useDefaultClone)
            {
                return (T)DeepCloneInner(obj);
            }
            return default(T);
        }
        /// 
        /// 内部深克隆方法
        /// 
        /// 
        /// 
        private static object DeepCloneInner(object obj)
        {
            MemoryStream stream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, obj);
            stream.Position = 0;
            return formatter.Deserialize(stream);
        }
        private static Dictionary> m_DeepCloneMap = new Dictionary>();
        /// 
        /// 获取深克隆方法
        /// 
        /// 
        /// 
        private static Func GetDeepCloneMethod(Type type)
        {
            var flag = m_DeepCloneMap.TryGetValue(type, out Func method);
            if (!flag)
            {
                var deepClone = type.GetMethod("DeepClone", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                if (deepClone != null)
                {
                    var source = Expression.Parameter(typeof(Object), "p");
                    var body_objToType = Expression.Convert(source, deepClone.DeclaringType);
                    MethodCallExpression cloneMethod = Expression.Call(body_objToType, deepClone);
                    var useValue = Expression.Convert(cloneMethod, typeof(object));
                    var deep = Expression.Lambda>(useValue, source).Compile();
                    m_DeepCloneMap[type] = deep;
                    return deep;
                }
                else
                {
                    //如果没有DeepClone直接赋值null,为了下一次查询时容易读到结果
                    m_DeepCloneMap[type] = null;
                    return null;
                }

            }
            return method;
        } 
        #endregion
    }

四、代码解析

如果您只需要一些可用的代码,上面的代码拷贝走就能用了。如果您还想听我瞎咧咧两句,那就继续看看下面的分析

1、浅克隆部分

       浅克隆用到了Object对象的MemberwiseClone方法,这个方法在非静态类里面直接调用很容易,这里有一个小难点就是要在对象外边调用这个受保护的方法,这里我有一点想告诉c#初学者,无论对象的成员是公有的,私有的还是受保护的,用反射都能取到。取到MemberwiseClone之后,缓存起来直接调用就可以了,但是反射调用方法速度不是很理想,我这里用lamda表达式将反射方法转换成了一个委托,这一步可以加快方法速度。当然速度不可能快过你在非静态类中直接调用该方法。

      深克隆,用到了一种约定的方式,而不是使用接口。约定只要类中实现了名为DeepClone的方法,当该工具类调用深克隆时,会使用实例的DeepClone方法。如果对象没有实现DeepClone方法,工具内部会使用默认的深度克隆方法对对象进行克隆。

内部的深度克隆方法是使用二进制序列化的方式实现的,当然如果想使用二进制序列化的类,需要给类加入可序列化标记 [Serializable]

 

 

你可能感兴趣的:(C#基础,实践“出”坑,软件工程,c#)