理解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);
        }
    }
}
相关推荐
落落落sss14 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
简单.is.good32 分钟前
【测试】接口测试与接口自动化
开发语言·python
Yvemil71 小时前
MQ 架构设计原理与消息中间件详解(二)
开发语言·后端·ruby
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
我是陈泽1 小时前
一行 Python 代码能实现什么丧心病狂的功能?圣诞树源代码
开发语言·python·程序员·编程·python教程·python学习·python教学
优雅的小武先生1 小时前
QT中的按钮控件和comboBox控件和spinBox控件无法点击的bug
开发语言·qt·bug
明耀1 小时前
WPF RadioButton 绑定boolean值
c#·wpf
虽千万人 吾往矣1 小时前
golang gorm
开发语言·数据库·后端·tcp/ip·golang
创作小达人1 小时前
家政服务|基于springBoot的家政服务平台设计与实现(附项目源码+论文+数据库)
开发语言·python
郭二哈1 小时前
C++——list
开发语言·c++·list