理解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);
        }
    }
}
相关推荐
hopetomorrow7 分钟前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php
小牛itbull17 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
广煜永不挂科24 分钟前
Devexpress.Dashboard的调用二义性
c#·express
请叫我欧皇i25 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
闲暇部落28 分钟前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
GIS瞧葩菜37 分钟前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
chnming198742 分钟前
STL关联式容器之set
开发语言·c++
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
GIS 数据栈1 小时前
每日一书 《基于ArcGIS的Python编程秘笈》
开发语言·python·arcgis
Mr.131 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++