继续上节的对象深拷贝,上节讲了通过序列化和反序列化来实现深度拷贝,这一节我们来讲述一下第二种深度拷贝的方法,反射。通过反射来实现深度拷贝。
反射主要是在运行时获取对象的元信息,System.Reflection命名空间允许我们在程序运行时来获取对象的信息、创建已存在类的实例,也能够获取对象的属性和执行对象的方法。
下面我创建一个静态的方法来接收任何类型的对象,并返回一个新对象的引用。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Reflection; 6 7 namespace ReflectionCopy 8 { 9 public class Utility 10 { 11 public static object CloneObject(object objsource) 12 { 13 //第一步获取原对象的类型,并创建一个同类型的对象 14 Type typesource = objsource.GetType(); 15 16 object objTarget = Activator.CreateInstance(typesource); 17 18 //第二步获取原对象的所有属性 19 20 PropertyInfo[] propertyinfo = typesource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 21 22 //第三步将原对象的所有属性赋给目标对象 23 24 foreach (PropertyInfo item in propertyinfo) 25 { 26 if (item.CanWrite) 27 { 28 29 //值类型,字符串,枚举类型直接把值复制,不存在浅拷贝 虽然字符串是引用类型,但是字符串是不可变的,每次都是创建新的字符串对象,所以不存在浅拷贝 30 if (item.PropertyType.IsEnum || item.PropertyType.IsValueType || item.PropertyType.Equals(typeof(System.String))) 31 { 32 33 item.SetValue(objTarget, item.GetValue(objsource, null), null); 34 } 35 else 36 { 37 object objpropertyvalue = item.GetValue(objsource, null); 38 39 item.SetValue(objTarget, CloneObject(objpropertyvalue), null); 40 } 41 } 42 } 43 return objTarget; 44 } 45 } 46 }
这个方法也可以做成一个扩展方法:
1 public static class ObjectExtension 2 { 3 public static object CloneObject(this object objSource) 4 { 5 //Get the type of source object and create a new instance of that type 6 Type typeSource = objSource.GetType(); 7 object objTarget = Activator.CreateInstance(typeSource); 8 9 //Get all the properties of source object type 10 PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 11 12 //Assign all source property to taget object 's properties 13 foreach (PropertyInfo property in propertyInfo) 14 { 15 //Check whether property can be written to 16 if (property.CanWrite) 17 { 18 //check whether property type is value type, enum or string type 19 if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(typeof(System.String))) 20 { 21 property.SetValue(objTarget, property.GetValue(objSource, null), null); 22 } 23 //else property type is object/complex types, so need to recursively call this method until the end of the tree is reached 24 else 25 { 26 object objPropertyValue = property.GetValue(objSource, null); 27 if (objPropertyValue == null) 28 { 29 property.SetValue(objTarget, null, null); 30 } 31 else 32 { 33 property.SetValue(objTarget, objPropertyValue.CloneObject(), null); 34 } 35 } 36 } 37 } 38 return objTarget; 39 } 40 }
现在我们调用该方法,看看结果:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ReflectionCopy 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 Employee emp = new Employee(); 13 14 emp.EmployeeId = 1000; 15 emp.EmployeeName = "IT少年"; 16 emp.Department = new Department { DepartmentId = 1, DepartmentName = "Examination" }; 17 18 Employee empclone = emp.CloneObject() as Employee; 19 20 emp.EmployeeId = 1003; 21 emp.EmployeeName = "TTT"; 22 emp.Department.DepartmentId = 3; 23 emp.Department.DepartmentName = "admin"; 24 Console.WriteLine("----emp原始对象------"); 25 Console.WriteLine("拷贝前DepartmentName应该是admin: " + emp.Department.DepartmentName); 26 Console.WriteLine("拷贝前DepartmentID应该是3: " + emp.Department.DepartmentId); 27 28 Console.WriteLine("----empclone拷贝对象------"); 29 Console.WriteLine("拷贝DepartmentName应该是Examination: " + empclone.Department.DepartmentName); 30 Console.WriteLine("拷贝DepartmentID应该是1: " + empclone.Department.DepartmentId); 31 Console.ReadKey(); 32 } 33 34 } 35 }
运行结果可以看出和序列化和反序列化深拷贝一样的。