本文主要介绍.NET(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);
        }
    }
}

输出结果:

httpswwwcjavapycom

推荐文档