Clone.gif
关于Clone一般区分为两种,浅拷贝和深拷贝。
浅拷贝
指的是拷贝一个对象的时候,只拷贝对对象的引用。当你修改一个对象的值后,另一个对象的值也会改变。在内存中引用类型的创建是创建在内存堆中,而内存栈中这是创建一个对内存堆中的地址的引用。值类型则是直接在内存栈中创建。
.net中实现浅拷贝的内置方法(System.Object的方法) MemberwiseClone()。
例如:
// 将要进行浅度复制的对象,注意为引用类型
public class RefLine : ICloneable
{
public RefPoint rPoint;
public ValPoint vPoint;
public RefLine(RefPoint rPoint, ValPoint vPoint)
{
this.rPoint = rPoint;
this.vPoint = vPoint;
}
public object Clone()
{
return this.MemberwiseClone();//.net中实现浅拷贝的内置方法(System.Object的方法)
}
}
// 定义一个引用类型成员
public class RefPoint
{
public int x;
public RefPoint(int x)
{
this.x = x;
}
}
// 定义一个值类型成员
public struct ValPoint
{
public int x;
public ValPoint(int x)
{
this.x = x;
}
}
/// <summary>
/// clone() 方法验证
/// </summary>
public static void demo2()
{
RefPoint rPoint = new RefPoint(1);
ValPoint vPoint = new ValPoint(1);
RefLine line = new RefLine(rPoint, vPoint);
RefLine newLine = (RefLine)line.Clone();
Console.WriteLine("Original: line.rPoint.x = {0}, line.vPoint.x= {1} ", line.rPoint.x, line.vPoint.x);
Console.WriteLine("Cloned: newLine.rPoint.x = {0}, newLine.vPoint.x = {1} ", newLine.rPoint.x, newLine.vPoint.x);
line.rPoint.x = 10; // 修改原先的line的引用类型成员 rPoint
line.vPoint.x = 10; // 修改原先的line的值类型成员 vPoint
Console.WriteLine("Original: line.rPoint.x = {0}, line.vPoint.x= {1} ", line.rPoint.x, line.vPoint.x);
Console.WriteLine("Cloned: newLine.rPoint.x = {0}, newLine.vPoint.x = {1} ", newLine.rPoint.x, newLine.vPoint.x);
}
static void Main(string[] args) {
demo2();
Console.Read();
}
结果如下:
ShallowClone.png
深拷贝:
指在内存堆中又创建一个和你Clone对象一样的对象。当你修改了旧对象中的某个值时,新对象也不会变。
深拷贝有多种实现方式:
1.深拷贝可以利用序列化反序列化对对象进行深度复制。
/// <summary>
/// 深拷贝
/// </summary>
/// <returns></returns>
public object Clone()
{
using(var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.Serialize(ms, this);
ms.Seek(0, SeekOrigin.Begin);
return (bf.Deserialize(ms));
}
}
注:使用序列化时记得在类上加上[serializable]的特性
2.利用反射的方式进行深度复制。
public static T DeepCopyByReflect<T>(T obj)
{
//如果是字符串或值类型则直接返回
if (obj is string || obj.GetType().IsValueType) return obj;
object retval = Activator.CreateInstance(obj.GetType());//若obj没有无参构造函数,此语句报错
FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (FieldInfo field in fields)
{
try { field.SetValue(retval, DeepCopyByReflect(field.GetValue(obj))); }//递归下去直到field为值类型或string给其赋值
catch { }
}
return (T)retval;
}
注:使用反射时必须引用类型必须有无参构造函数
两种方式的结果相同,如下: