理解C#中对象的浅拷贝和深拷贝

本文章主要介绍C#中对象的拷贝,其中包括浅拷贝和深拷贝,以及浅拷贝和深拷贝的实现方式,不同的实现方式之间的性能对比。

1、浅拷贝和深拷贝

浅拷贝是指将对象中的数值类型的字段拷贝到新的对象中,而对象中的引用型字段则指复制它的一个引用到目标对象。如果改变目标对象中引用型字段的值他将反映在原是对象中,也就是说原始对象中对应的字段也会发生变化。深拷贝与浅拷贝不同的是对于引用的处理,深拷贝将会在新对象中创建一个新的和原是对象中对应字段相同(内容相同)的字段,也就是说这个引用和原是对象的引用是不同的,我们在改变新对象中的这个字段的时候是不会影响到原始对象中对应字段的内容。

2、浅拷贝实现方式

1)new对象赋值

[Serializable]
class Employee
{
    public string ID { get; set; }
    public int Age { get; set; }
    public Department DepartmentName { get; set; }
}
[Serializable]
class Department
{
    public string DepartmentName { get; set; }
    public Department(string value)
    {
        DepartmentName = value;
    }
    public override string ToString()
    {
        return DepartmentName.ToString();
    }
}
Employee emp1 = new Employee()
{
    ID = "cjavapy",
    Age = 20,
    DepartmentName = new Department("develop")
}
;
Employee emp2=new Employee()
{
    ID=emp1.ID,
    Age=emp1.Age,
    DepartmentName=emp1.DepartmentName
};

2)实现ICloneable接口

class Employee : ICloneable
{
    public string ID { get; set; }
    public int Age { get; set; }
    public Department DepartmentName { get; set; }
    //实现ICloneable接口的Clone方法
    public object Clone()
    {
        return this.MemberwiseClone();//浅拷贝
    }
}
class Department
{
    public string DepartmentName { get; set; }
    public Department(string value)
    {
        DepartmentName = value;
    }
    public override string ToString()
    {
        return DepartmentName.ToString();
    }
}

浅拷贝:

Employee emp1 = new Employee()
{
    ID = "cjavapy",
    Age = 20,
    DepartmentName = new Department("develop")
};
Employee emp2 = emp1.Clone() as Employee;//浅拷贝

3、深拷贝实现方式

1)二进制序列化

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using Newtonsoft.Json;

namespace ConsoleApplication
{
    public class Utils
    {

        public static T BinaryClone<T>(T source)
        {
            if (!typeof(T).IsSerializable)
            {
                throw new ArgumentException("需要添加[Serializable]标签", "source");
            }
            if (Object.ReferenceEquals(source, null))
            {
                return default(T);
            }
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();
            using (stream)
            {
                formatter.Serialize(stream, source);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }
    }
    
}

2)JSON序列化

using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using Newtonsoft.Json;

namespace ConsoleApplication
{
    public class Utils
    {
        /// 
        /// 序列化反序列化方式
        /// 
        /// 
        /// 
        public static TOut JsonClone<TIn,TOut>(TIn tIn)
        {
            return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(tIn));
        }
    }
    
}

3)Reflection反射

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    public class Utils
    {
         /// 
        /// 反射实现深拷贝
        /// 
        /// 传入TIn对象返回TOut对象
        /// 
        public static TOut ReflectionClone<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;
        }
        /// <summary>
        /// 传入List<TIn>,返回List<TOut>
        /// </summary>
        /// <typeparam name="TIn"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="tInList"></param>
        /// <returns></returns>
        public static List<TOut> ReflectionCloneList<TIn, TOut>(List<TIn> tInList)
        {
            List<TOut> result = new List<TOut>();
            foreach (var tIn in tInList)
            {
                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));
                }
                result.Add(tOut);
            }
            return result;
        }
    }
    public class ContactPerson
    {
        public string Name { get; set; }
        public string MobileNum { get; set; }

    }
    class Program
    {

        static void Main(string[] args)
        {
            var persons = new List<ContactPerson>
        {
                new ContactPerson { Name= "C", MobileNum = "13756863001"},
                new ContactPerson { Name = "C#", MobileNum = "13756863002"},
                new ContactPerson { Name = "Java", MobileNum = "13756863003"}
            };
          var result = Utils.ReflectionCloneList<ContactPerson, ContactPerson>(persons);
          foreach(var p in result)
            Console.WriteLine("姓名: {0} 号码为: {1}", p.Name, p.MobileNum);
         Console.Read();
        }
    }
}

4)XML序列化

using System.IO;
using System.Xml.Serialization;

namespace ConsoleApplication
{
    public class Utils
    {
        public static T DeserializeXML<T>(string xmlData) where T : new()
        {
            if (string.IsNullOrEmpty(xmlData))
                return default(T);
            TextReader tr = new StringReader(xmlData);
            T DocItms = new T();
            XmlSerializer xms = new XmlSerializer(DocItms.GetType());
            DocItms = (T)xms.Deserialize(tr);
            return DocItms == null ? default(T) : DocItms;
        }
    }
}

5)表达式目录树

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    /// <summary>
    /// 生成表达式目录树  泛型缓存
    /// </summary>
    /// <typeparam name="TIn"></typeparam>
    /// <typeparam name="TOut"></typeparam>
    public class ExpressionGenericMapper<TIn, TOut>//`2
    {
        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);
        }
    }
}

4、拷贝方式性能对比

对比代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Newtonsoft.Json;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Employee emp1 = new Employee()
            {
                ID = "cjavapy",
                Age = 20,
                DepartmentName = new Department("develop")
            };
            long common = 0;
            long expression = 0;
            long json = 0;
            long xml = 0;
            long binary = 0;
            long reflection = 0;
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                for (int i = 0; i < 1000000; i++)
                {
                    Employee copy = BinaryClone<Employee>(emp1);
                }
                watch.Stop();
                binary = watch.ElapsedMilliseconds;
            }
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                for (int i = 0; i < 1000000; i++)
                {
                    Employee copy = ReflectionClone<Employee, Employee>(emp1);
                }
                watch.Stop();
                reflection = watch.ElapsedMilliseconds;
            }
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                for (int i = 0; i < 1000000; i++)
                {

                    Employee copy = DeserializeXML<Employee>(SerializeXML<Employee>(emp1));
                }
                watch.Stop();
                xml = watch.ElapsedMilliseconds;
            }
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                for (int i = 0; i < 1000000; i++)
                {
                    Employee copy = JsonClone<Employee, Employee>(emp1);
                }
                watch.Stop();
                json = watch.ElapsedMilliseconds;
            }
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                for (int i = 0; i < 1000000; i++)
                {
                    Employee copy = ExpressionGeneric<Employee, Employee>.Clone(emp1);
                }
                watch.Stop();
                expression = watch.ElapsedMilliseconds;
            }

            Console.WriteLine($"binary = { binary} ms");
            Console.WriteLine($"reflection = { reflection} ms");
            Console.WriteLine($"serialize = { xml} ms");
            Console.WriteLine($"json = { json} ms");
            Console.WriteLine($"generic = { expression} ms");
            Console.ReadKey();
        }
        public static T BinaryClone<T>(T source)
        {
            if (!typeof(T).IsSerializable)
            {
                throw new ArgumentException("需要添加[Serializable]标签", "source");
            }

            if (Object.ReferenceEquals(source, null))
            {
                return default(T);
            }

            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();
            using (stream)
            {
                formatter.Serialize(stream, source);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }
        /// <summary>
        /// 反射
        /// </summary>
        /// <typeparam name="TIn"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="tIn"></param>
        /// <returns></returns>
        public static TOut ReflectionClone<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;
        }
        public static TOut JsonClone<TIn, TOut>(TIn tIn)
        {
            return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(tIn));
        }
        public static string SerializeXML<T>(T t)
        {
            using (StringWriter sw = new StringWriter())
            {
                XmlSerializer xz = new XmlSerializer(t.GetType());
                xz.Serialize(sw, t);
                return sw.ToString();
            }
        }
        public static T DeserializeXML<T>(string xmlData) where T : new()
        {
            if (string.IsNullOrEmpty(xmlData))
                return default(T);

            TextReader tr = new StringReader(xmlData);
            T DocItms = new T();
            XmlSerializer xms = new XmlSerializer(DocItms.GetType());
            DocItms = (T)xms.Deserialize(tr);

            return DocItms == null ? default(T) : DocItms;
        }

    }
    [Serializable]
    public class Employee : ICloneable
    {
        public string ID { get; set; }
        public int Age { get; set; }
        public Department DepartmentName { get; set; }

        //实现ICloneable接口的Clone方法
        public object Clone()
        {
            return this.MemberwiseClone();//浅拷贝
        }
    }
    [Serializable]
    public class Department
    {
        public string DepartmentName { get; set; }
        public Department()
        {

        }
        public Department(string value)
        {
            DepartmentName = value;
        }
        public override string ToString()
        {
            return DepartmentName.ToString();
        }
    }
    /// <typeparam name="TIn"></typeparam>
    /// <typeparam name="TOut"></typeparam>
    public class ExpressionGeneric<TIn, TOut>//Mapper`2
    {
        private static Func<TIn, TOut> _FUNC = null;
        static ExpressionGeneric()
        {
            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 Clone(TIn t)
        {
            return _FUNC(t);
        }
    }
}
相关推荐
王俊山IT12 分钟前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
为将者,自当识天晓地。14 分钟前
c++多线程
java·开发语言
小政爱学习!16 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
k093331 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
神奇夜光杯39 分钟前
Python酷库之旅-第三方库Pandas(202)
开发语言·人工智能·python·excel·pandas·标准库及第三方库·学习与成长
Themberfue41 分钟前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
plmm烟酒僧43 分钟前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv
测试界的酸菜鱼1 小时前
Python 大数据展示屏实例
大数据·开发语言·python
晨曦_子画1 小时前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
Black_Friend1 小时前
关于在VS中使用Qt不同版本报错的问题
开发语言·qt