.NET(C#) 对象的拷贝(浅拷贝和深拷贝)
本文主要介绍.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); } } }
6. mspack序列化
MessagePack是一种高效二进制序列化格式。可以在多种语言中进行快速数据交换,比如JSON格式等。它比Json更加小巧,更加高效,可以用于一些结构化数据存储 ,非常适合适用于消息总线,MemoryCache等对序列化要求比较高的场合。
MessagePack基本上被大多数主流语言支持,因此用它来作为教程间交换的消息序列化也是一种不错的方式,本文这里简单的介绍一下如何在.Net 中使用MessagePack。
首先安装MessagePack和MessagePackAnalyzer的Nuget包:
Install
-Package
MessagePack
Install
-Package
MessagePackAnalyzer
其中MessagePackAnalyzer是可选的,但它可以帮助检查序列化标记的正确性,还是非常有用的。
// mark MessagePackObjectAttribute [MessagePackObject] public class MyClass { // Key is serialization index, it is important for versioning. [Key(0)] public int Age { get; set; } [Key(1)] public string FirstName { get; set; } [Key(2)] public string LastName { get; set; } // public members and does not serialize target, mark IgnoreMemberttribute [IgnoreMember] public string FullName { get { return FirstName + LastName; } } } class Program { static void Main(string[] args) { var mc = new MyClass { Age = 99, FirstName = "hoge", LastName = "huga", }; // call Serialize/Deserialize, that's all. var bytes = MessagePackSerializer.Serialize(mc); var mc2 = MessagePackSerializer.Deserialize<MyClass>(bytes); // you can dump msgpack binary to human readable json. // In default, MeesagePack for C# reduce property name information. // [99,"hoge","huga"] var json = MessagePackSerializer.ToJson(bytes); Console.WriteLine(json); } }
拷贝方式性能对比
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); } } }