在本文中,我们比较了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#教程。
