通过GetCopy()方法,介绍了如何使用受保护的方法System.Object.MemberwiseClone()进行浅度复制。
public class Cloner
{
public int Val;
public Cloner{int newVal} => Val = newVal;
public object GetCopy() => MemberwiseClone();
}
假定有一些引用类型的字段,而不是值类型的字段(例如:对象):
public class Content
{
public int Val;
}
public class Cloner
{
public Content MyContent = new Content();
public Cloner(int newVal) => MyContent.Val = newVal;
public object GetCopy() => MemberwiseClone();
}
此时,通过GetCopy()得到的浅度复制包括一个字段,它引用的对象与源对象相同。以下代码使用这个Cloner类来说明浅度复制引用类型的结果:
Cloner mySource = new Cloner(5);
Cloner myTarget = (Cloner)mySource.GetCopy();
WriteLine($"myTarget.MyContent.Val = {myTarget.MyContent.Val}");
mySource.MyContent.Val = 2;
WriteLine($"myTarget.MyContent.Val = {myTarget.MyContent.Val}");
第4行把一个值赋给mySource.MyContent.Val,它是源对象中公共字段MyContent的公共字段Val。这也该变了myTarget.MyContent.Val的值。这是因为mySource.MyContent引用了与myTarget.MyContent相同的对象实例。上述代码的输出结果如下:
myTarget.MyContent.Val = 5
myTarget.MyContent.Val = 2
为了解决这个问题,需要执行深度复制。修改上面的GetCopy()方法就可以进行深度复制,但最好使用.NETFramework的标准方式:实现ICloneable接口,该接口有一个Clone()方法:这个方法不带参数,返回一个object类型的结果,其签名和上面使用的GetCopy()方法相同。
为了修改上面的类,可使用下面的深度复制代码:
public class Content
{
public int Val;
}
public class Cloner : ICloneacle
{
public Content MyContent => new Content();
public Cloner(int newVal) => MyContent.Val = newVal;
public object Clone()
{
Cloner clonedCloner = new Cloner(MyContent.Val);
return cloneCloner;
}
}
其中使用包含在源Cloner对象中的Content对象(MyContent)的Val字段,创建了一个新对象Cloner。这个字段是一个值类型,所以不需要深度复制。
使用与上面类似的代码来测试浅度复制,但用Clone()替代GetCopy(),得到如下结果:
myTarget.MyContent.Val = 5
myTarget.MyContent.Val = 5
这次包含的对象是独立的。注意有时在比较复杂的对象系统中,调用Clone()是一个递归过程。例如,如果Cloner类的MyContent字段也需要深度复制,就要使用下面的代码:
public class Cloner : IClonerable
{
public Content myContent = new Content();
...
public object Clone()
{
Cloner clonedCloner = new Cloner();
clonedCloner.MyContent = MyContent.Clone();
return cloneCloner;
}
}
这里调用了默认的构造函数,以便简化创建新对象Cloner的语法。为使这段代码能正常工作,还需要再Content类上实现ICloneable接口。