在本文中,我们比较了C#中对象的浅拷贝和深拷贝。
数据的复制是编程中的一项重要任务。对象是OOP中的复合数据类型。对象中的成员字段可以按值或按引用存储。可以通过两种方式执行复制。
浅拷贝将所有值和引用复制到一个新实例中。引用指向的数据不会被复制;只有指针被复制。新引用指向原始对象。对引用成员的任何更改都会影响这两个对象。
深拷贝将所有值复制到一个新实例中。对于存储为引用的成员,深拷贝执行被引用数据的深拷贝。创建引用对象的新副本。并存储指向新创建对象的指针。对这些引用对象的任何更改都不会影响该对象的其他副本。深拷贝是完全复制的对象。
如果成员字段是值类型,则对该字段进行逐位复制。如果该字段是引用类型,则复制引用但不复制引用对象;因此,原始对象中的引用和克隆中的引用指向同一个对象。
C#浅拷贝示例
以下程序执行浅拷贝。
namespace ShallowCopy; class Color { public int red; public int green; public int blue; public Color(int red, int green, int blue) { this.red = red; this.green = green; this.blue = blue; } } class MyObject : ICloneable { public int id; public string size; public Color col; public MyObject(int id, string size, Color col) { this.id = id; this.size = size; this.col = col; } public object Clone() { return new MyObject(this.id, this.size, this.col); } public override string ToString() { var s = String.Format("id: {0}, size: {1}, color:({2}, {3}, {4})", this.id, this.size, this.col.red, this.col.green, this.col.blue); return s; } } class Program { static void Main(string[] args) { var col = new Color(23, 42, 223); var obj1 = new MyObject(23, "small", col); var obj2 = (MyObject)obj1.Clone(); obj2.id += 1; obj2.size = "big"; obj2.col.red = 255; Console.WriteLine(obj1); Console.WriteLine(obj2); } }
这是一个浅拷贝的例子。我们定义了两个自定义对象:MyObject
和Color
。MyObject
对象将引用Color对象。
class MyObject : ICloneable
我们应该为我们要克隆的对象实现ICloneable
接口。
public object Clone() { return new MyObject(this.id, this.size, this.col); }
ICloneable
接口迫使我们创建一个Clone
方法。此方法返回一个具有复制值的新对象。
var col = new Color(23, 42, 223);
我们创建Color对象的一个实例。
var obj1 = new MyObject(23, "small", col);
创建了MyObject
类的实例。Color
对象的实例被传递给构造函数。
var obj2 = (MyObject) obj1.Clone();
我们创建obj1对象的浅拷贝并将其分配给obj2变量。Clone
方法返回一个Object
,我们期望MyObject
。这就是我们进行显式转换的原因。
obj2.id += 1; obj2.size = "big"; obj2.col.red = 255;
这里我们修改复制对象的成员字段。我们增加id,将大小更改为“大”并更改颜色对象的红色部分。
Console.WriteLine(obj1); Console.WriteLine(obj2);
Console.WriteLine
方法调用obj2
对象的ToString
方法,返回对象的字符串表示形式。
$ dotnet run id: 23, size: small, color:(255, 42, 223) id: 24, size: big, color:(255, 42, 223)
我们可以看到ID不同(23对24)。尺寸不同(“小”与“大”)。但是颜色对象的红色部分对于两个实例(255)都是相同的。更改克隆对象的成员值(id、大小)不会影响原始对象。更改引用对象(col)的成员也影响了原始对象。换句话说,两个对象都引用内存中的同一个颜色对象。
C#深拷贝示例
为了改变这种行为,我们接下来进行深拷贝。
namespace DeepCopy; class Color : ICloneable { public int red; public int green; public int blue; public Color(int red, int green, int blue) { this.red = red; this.green = green; this.blue = blue; } public object Clone() { return new Color(this.red, this.green, this.blue); } } class MyObject : ICloneable { public int id; public string size; public Color col; public MyObject(int id, string size, Color col) { this.id = id; this.size = size; this.col = col; } public object Clone() { return new MyObject(this.id, this.size, (Color)this.col.Clone()); } public override string ToString() { var s = String.Format("id: {0}, size: {1}, color:({2}, {3}, {4})", this.id, this.size, this.col.red, this.col.green, this.col.blue); return s; } } class Program { static void Main(string[] args) { var col = new Color(23, 42, 223); var obj1 = new MyObject(23, "small", col); var obj2 = (MyObject)obj1.Clone(); obj2.id += 1; obj2.size = "big"; obj2.col.red = 255; Console.WriteLine(obj1); Console.WriteLine(obj2); } }
在这个程序中,我们对对象执行深拷贝。
class Color : ICloneable
现在Color类实现了ICloneable
接口。
public object Clone() { return new Color(this.red, this.green, this.blue); }
Color
类也有一个Clone
方法。这有助于创建引用对象的副本。
public object Clone() { return new MyObject(this.id, this.size, (Color) this.col.Clone()); }
当我们克隆MyObject
时,我们在col引用类型上调用Clone
方法。这样我们也有一个颜色值的副本。
$ dotnet run id: 23, size: small, color:(23, 42, 223) id: 24, size: big, color:(255, 42, 223)
现在引用的Color对象的红色部分不一样了。原始对象保留了之前的值(23)。
在本文中,我们比较了C#中对象的浅拷贝和深拷贝。
列出所有C#教程。