C#深拷贝的三种实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq826364410/article/details/86319782

浅拷贝与深拷贝:

浅拷贝:指的是拷贝一个对象时,仅仅拷贝对象的引用,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。

深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。

C#深拷贝的有三种实现:反射、序列化和表达式树。

一、当要拷贝的类没有互相引用时

反射实现:

// 利用反射实现深拷贝
        public static T DeepCopyWithReflection<T>(T obj)
        {
            Type type = obj.GetType();

            // 如果是字符串或值类型则直接返回
            if (obj is string || type.IsValueType) return obj;
            // 如果是数组
            if (type.IsArray)
            {
                Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
                var array = obj as Array;
                Array copied = Array.CreateInstance(elementType, array.Length);
                for (int i = 0; i < array.Length; i++)
                {
                    copied.SetValue(DeepCopyWithReflection(array.GetValue(i)), i);
                }
                return (T)Convert.ChangeType(copied, obj.GetType());
            }

            object retval = Activator.CreateInstance(obj.GetType());

            PropertyInfo[] properties = obj.GetType().GetProperties(
                BindingFlags.Public | BindingFlags.NonPublic
                | BindingFlags.Instance | BindingFlags.Static);
            foreach (var property in properties)
            {
                var propertyValue = property.GetValue(obj, null);
                if (propertyValue == null)
                    continue;
                property.SetValue(retval, DeepCopyWithReflection(propertyValue), null);
            }

            return (T)retval;
        }

序列化实现:分为xml序列化和二进制序列化,json(没有C#内部实现,需要引用外部的包)等

// 利用XML序列化和反序列化实现
        public static T DeepCopyWithXmlSerializer<T>(T obj)
        {
            object retval;
            using (MemoryStream ms = new MemoryStream())
            {
                XmlSerializer xml = new XmlSerializer(typeof(T));
                xml.Serialize(ms, obj);
                ms.Seek(0, SeekOrigin.Begin);
                retval = xml.Deserialize(ms);
                ms.Close();
            }

            return (T)retval;
        }

        // 利用二进制序列化和反序列实现
        public static T DeepCopyWithBinarySerialize<T>(T obj)
        {
            object retval;
            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();
                // 序列化成流
                bf.Serialize(ms, obj);
                ms.Seek(0, SeekOrigin.Begin);
                // 反序列化成对象
                retval = bf.Deserialize(ms);
                ms.Close();
            }

            return (T)retval;
        }

表达式树实现:

表达式可以是一个参数(如参数x),一个常数(如常数5),一个加运算(如x+5)等等,可以把几个小的表达式组装在一起成为大的表达式,例如:(x+5)-(++y)。对于这样一个表达式可以用一棵树来表示,如下:

这就是表达式树,表达式树本身也是一个表达式(大的表达式)。一个表达式也是一棵表达式树,可以说它是一棵小的表达式树。可以把表达式树和表达式认为是一个东西,C#中都用Expression类表示。

表达式树的创建
Lambda表达式方法
表达式可以通过Lambda表示创建Expression<TDelegate>类型,如下:

Expression<Func<int, int,bool>> fun = (x, y) => x < y

这种方法创建出的表达式根节点类型为ExpressionType.Lambda,Type类型为返回值类型typeof(bool)。

组装法
另一种创建表示的方式是通过Expression类的静态函数“组装”表达式如下:

 ParameterExpression p1 = Expression.Parameter(typeof(int),"x");

 ParameterExpression p2 = Expression.Parameter(typeof(int),"y");

  BinaryExpression expr =Expression.GreaterThan(p1, p2);

我们先创建了两个参数表达式x, y然后用GreaterThan组装在一起,最终的表达式为“x>y”,expr的节点类型为LessThan,Type类型为typeof(bool)

Expression类中有我们所需要所有“组装”表达式的用的静态函数,我们不能直接用new的方法去创建表达式节点,表达式所有的节点需要用Expression类中的静态函数创建。

// 表达式树实现
    public class DeepCopyExp<TIn, TOut>
    {
        private static readonly Func<TIn, TOut> Cache = GetExp();

        private static Func<TIn, TOut> GetExp()
        {
            ParameterExpression expression = Expression.Parameter(typeof(TIn), "p");
            List<MemberBinding> member = new List<MemberBinding>();

            foreach (var item in typeof(TOut).GetProperties())
            {
                if (!item.CanWrite)
                    continue;

                MemberExpression property = Expression.Property(expression, typeof(TIn).GetProperty(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                member.Add(memberBinding);
            }

            MemberInitExpression memberInitExpression =
                Expression.MemberInit(Expression.New(typeof(TOut)), member.ToArray());
            Expression<Func<TIn, TOut>> lambda =
                Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new[] { expression });
            return lambda.Compile();
        }

        public static TOut Copy(TIn tIn)
        {
            return Cache(tIn);
        }
    }

小结:

调用一百万次的时间排序:表达式树最快,反射次之,序列化最慢。

二、当要拷贝的类有互相引用时

经过测试,只有经过修改的反射实现和二进制序列化,可以保证对象引用的正确性。当要拷贝的类有互相引用时,xml序列化会直接报错。表达式树,处理互相引用的类也会有问题,可能表达式树的代码也需要相应才可以。

反射实现:用一个字典来存放每个对象的反射次数来避免反射代码的循环递归

public class DeepCopyHelper
    {
        // 用一个字典来存放每个对象的反射次数来避免反射代码的循环递归
        static Dictionary<Type, int> typereflectionCountDic = new Dictionary<Type, int>();
        static object DeepCopyDemoClasstypeRef = null;

        // 利用反射实现深拷贝
        public static T DeepCopyWithReflection<T>(T obj)
        {
            Type type = obj.GetType();

            // 如果是字符串或值类型则直接返回
            if (obj is string || type.IsValueType) return obj;
            // 如果是数组
            if (type.IsArray)
            {
                Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
                var array = obj as Array;
                Array copied = Array.CreateInstance(elementType, array.Length);
                for (int i = 0; i < array.Length; i++)
                {
                    copied.SetValue(DeepCopyWithReflection(array.GetValue(i)), i);
                }
                return (T)Convert.ChangeType(copied, obj.GetType());
            }

            object retval = Activator.CreateInstance(obj.GetType());

            PropertyInfo[] properties = obj.GetType().GetProperties(
                BindingFlags.Public | BindingFlags.NonPublic
                | BindingFlags.Instance | BindingFlags.Static);
            foreach (var property in properties)
            {
                var propertyValue = property.GetValue(obj, null);
                if (propertyValue == null)
                    continue;
                property.SetValue(retval, DeepCopyWithReflection(propertyValue), null);
            }

            return (T)retval;
        }

        public static T DeepCopyWithReflection_Second<T>(T obj)
        {
            Type type = obj.GetType();

            // 如果是字符串或值类型则直接返回
            if (obj is string || type.IsValueType) return obj;
            // 如果是数组
            if (type.IsArray)
            {
                Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
                var array = obj as Array;
                Array copied = Array.CreateInstance(elementType, array.Length);
                for (int i = 0; i < array.Length; i++)
                {
                    copied.SetValue(DeepCopyWithReflection(array.GetValue(i)), i);
                }

                return (T)Convert.ChangeType(copied, obj.GetType());
            }

            int reflectionCount = Add(typereflectionCountDic, obj.GetType());
            if (reflectionCount > 1 && obj.GetType() == typeof(DeepCopyDemoClass))
                return (T)DeepCopyDemoClasstypeRef; // 返回deepCopyClassB对象

            object retval = Activator.CreateInstance(obj.GetType());

            if (retval.GetType() == typeof(DeepCopyDemoClass))
                DeepCopyDemoClasstypeRef = retval; // 保存一开始创建的DeepCopyDemoClass对象

            PropertyInfo[] properties = obj.GetType().GetProperties(
                BindingFlags.Public | BindingFlags.NonPublic
                | BindingFlags.Instance | BindingFlags.Static);
            foreach (var property in properties)
            {
                var propertyValue = property.GetValue(obj, null);
                if (propertyValue == null)
                    continue;
                property.SetValue(retval, DeepCopyWithReflection_Second(propertyValue), null);
            }

            return (T)retval;
        }

        private static int Add(Dictionary<Type, int> dict, Type key)
        {
            if (key.Equals(typeof(String)) || key.IsValueType) return 0;
            if (!dict.ContainsKey(key))
            {
                dict.Add(key, 1);
                return dict[key];
            }

            dict[key] += 1;
            return dict[key];
        }
}

Main方法:调用代码以及测试的类

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            DeepCopyDemoClass deepCopyClassA = new DeepCopyDemoClass();
            deepCopyClassA.Name = "DeepCopyClassDemo";
            deepCopyClassA.pIntArray = new int[] { 1, 2 };
            deepCopyClassA.DemoEnum = DemoEnum.EnumA;
            deepCopyClassA.Address = new Address() { City = "Shanghai" };

            deepCopyClassA.TestB = new TestB() { Property1 = "TestProperty", DeepCopyClass = deepCopyClassA };

            // 使用反射来完成深拷贝
            //DeepCopyDemoClass deepCopyClassB = DeepCopyHelper.DeepCopyWithBinarySerialize<DeepCopyDemoClass>(deepCopyClassA);
            DeepCopyDemoClass deepCopyClassB = DeepCopyHelper.DeepCopyWithReflection_Second<DeepCopyDemoClass>(deepCopyClassA);
            //DeepCopyDemoClass deepCopyClassB = DeepCopyExp<DeepCopyDemoClass, DeepCopyDemoClass>.Copy(deepCopyClassA);
            //DeepCopyDemoClass deepCopyClassB = DeepCopyHelper.DeepCopyWithXmlSerializer<DeepCopyDemoClass>(deepCopyClassA);

            deepCopyClassB.id = 1;
            Console.WriteLine(string.Format("    id->[A: {0}] [B: {1}]", deepCopyClassA.id, deepCopyClassB.id));
            deepCopyClassB.Name = "DeepCopyClassDemoB";
            Console.WriteLine(string.Format("    Name->[A:{0}] [B:{1}]", deepCopyClassA.Name, deepCopyClassB.Name));
            deepCopyClassB.pIntArray[0] = 2;
            Console.WriteLine(string.Format("    intArray->[A:{0}] [B:{1}]", deepCopyClassA.pIntArray[0], deepCopyClassB.pIntArray[0]));
            deepCopyClassB.Address = new Address() { City = "Beijing" };
            Console.WriteLine(string.Format("    Addressstruct->[A: {0}] [B: {1}]", deepCopyClassA.Address.City, deepCopyClassB.Address.City));
            deepCopyClassB.DemoEnum = DemoEnum.EnumB;
            Console.WriteLine(string.Format("    DemoEnum->[A: {0}] [B: {1}]", deepCopyClassA.DemoEnum, deepCopyClassB.DemoEnum));
            deepCopyClassB.TestB.Property1 = "TestPropertyB";
            Console.WriteLine(string.Format("    Property1->[A:{0}] [B:{1}]", deepCopyClassA.TestB.Property1, deepCopyClassB.TestB.Property1));
            Console.WriteLine(string.Format("    TestB.DeepCopyClass.Name->[A:{0}] [B:{1}]", deepCopyClassA.TestB.DeepCopyClass.Name, deepCopyClassB.TestB.DeepCopyClass.Name));
            Console.ReadKey();
        }
    }

    [Serializable]
    public class DeepCopyDemoClass
    {
        public int id;
        public string Name;
        public int[] pIntArray { get; set; }
        public Address Address { get; set; }
        public DemoEnum DemoEnum { get; set; }

        // DeepCopyDemoClass中引用了TestB对象,TestB类又引用了DeepCopyDemoClass对象,从而造成了相互引用
        public TestB TestB { get; set; }
    }

    [Serializable]
    public class TestB
    {
        public string Property1 { get; set; }
        public DeepCopyDemoClass DeepCopyClass { get; set; }
    }

    [Serializable]
    public struct Address
    {
        public string City { get; set; }
    }

    [Serializable]
    public enum DemoEnum
    {
        EnumA = 0,
        EnumB = 1
    }
}

附录:

浅拷贝例子:

    public class Person
    {
        public string Name { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Person sourceP = new Person() { Name = "张三" };
            Person copyP = sourceP; // 浅拷贝
            copyP.Name = "小明"; // 拷贝对象改变Name值
            // 结果都是"小明",因为实现的是浅拷贝,一个对象的改变都会影响到另一个对象
            Console.WriteLine("Person.Name: [SourceP: {0}] [CopyP:{1}]", sourceP.Name, copyP.Name);
            Console.Read();
        }
    }

引用:

http://www.cnblogs.com/zhili/p/DeepCopy.html

https://www.cnblogs.com/emrys5/p/expression_trans_model.html

猜你喜欢

转载自blog.csdn.net/qq826364410/article/details/86319782
今日推荐