?
也許會(huì)有人這樣解釋C# 中淺拷貝與深拷貝區(qū)別:
淺拷貝是對(duì)引用類(lèi)型拷貝地址,對(duì)值類(lèi)型直接進(jìn)行拷貝。
不能說(shuō)它完全錯(cuò)誤,但至少還不夠嚴(yán)謹(jǐn)。比如:string 類(lèi)型咋說(shuō)?
其實(shí),我們可以通過(guò)實(shí)踐來(lái)尋找答案。
首先,定義以下類(lèi)型:
int 、string 、enum 、struct 、class 、int[ ] 、string[ ]
代碼如下:
//枚舉
public enum myEnum
{ _1 = 1, _2 = 2 }
//結(jié)構(gòu)體
public struct myStruct
{
public int _int;
public myStruct(int i)
{ _int = i; }
}
//類(lèi)
class myClass
{
public string _string;
public myClass(string s)
{ _string = s; }
}
//ICloneable:創(chuàng)建作為當(dāng)前實(shí)例副本的新對(duì)象。
class DemoClass : ICloneable
{
public int _int = 1;
public string _string = “1”;
public myEnum _enum = myEnum._1;
public myStruct _struct = new myStruct(1);
public myClass _class = new myClass(“1”);
//數(shù)組
public int[] arrInt = new int[] { 1 };
public string[] arrString = new string[] { “1” };
//返回此實(shí)例副本的新對(duì)象
public object Clone()
{
//MemberwiseClone:返回當(dāng)前對(duì)象的淺表副本(它是Object對(duì)象的基方法)
return this.MemberwiseClone();
}
}
注意:
ICloneable 接口:支持克隆,即用與現(xiàn)有實(shí)例相同的值創(chuàng)建類(lèi)的新實(shí)例。
MemberwiseClone 方法:創(chuàng)建當(dāng)前 System.Object 的淺表副本。
接下來(lái),構(gòu)建實(shí)例A ,并對(duì)實(shí)例A 克隆產(chǎn)生一個(gè)實(shí)例B。
然后,改變實(shí)例B 的值,并觀察實(shí)例A 的值會(huì)不會(huì)被改變。
代碼如下:
class 淺拷貝與深拷貝
{
static void Main(string[] args)
{
DemoClass A = new DemoClass();
//創(chuàng)建實(shí)例A的副本 --》 新對(duì)象實(shí)例B
DemoClass B = (DemoClass)A.Clone();
B._int = 2;
Console.WriteLine(“ int \t\t A:{0} B:{1}”, A._int, B._int);
B._string = “2”;
Console.WriteLine(“ string \t A:{0} B:{1}”, A._string, B._string);
B._enum = myEnum._2;
Console.WriteLine(“ enum \t\t A:{0} B:{1}”, (int)A._enum, (int)B._enum);
B._struct._int = 2;
Console.WriteLine(“ struct \t A:{0} B:{1}”,
A._struct._int, B._struct._int);
B._class._string = “2”;
Console.WriteLine(“ class \t\t A:{0} B:{1}”,
A._class._string, B._class._string);
B.arrInt[0] = 2;
Console.WriteLine(“ intArray \t A:{0} B:{1}”,
A.arrInt[0], B.arrInt[0]);
B.arrString[0] = “2”;
Console.WriteLine(“ stringArray \t A:{0} B:{1}”,
A.arrString[0], B.arrString[0]);
Console.ReadKey();
}
}
結(jié)果如下:
從最后的輸出結(jié)果,我們得知:
對(duì)于內(nèi)部的Class 對(duì)象和數(shù)組,則Copy 一份地址。[ 改變B 時(shí),A也被改變了 ]
而對(duì)于其它內(nèi)置的int / string / enum / struct / object 類(lèi)型,則Copy 一份值。
有一位網(wǎng)友說(shuō):string 類(lèi)型雖然是引用類(lèi)型,但是很多情況下.Net 把string 做值類(lèi)型來(lái)處理,我覺(jué)得string 應(yīng)該也是按照值類(lèi)型處理的。
這說(shuō)明他對(duì)string 類(lèi)型還不夠了解。
可以肯定的是:string 一定是引用類(lèi)型。那它為什么是深拷貝呢?
如果你看一下string 類(lèi)型的源代碼就知道了:
//表示空字符串。此字段為只讀。
public static readonly string Empty;
答案就在于 string 是 readonly 的,當(dāng)改變 string 類(lèi)型的數(shù)據(jù)值時(shí),將重新分配了內(nèi)存地址。
下面引用一段網(wǎng)友的代碼:Vseen[ Aloner ] 的個(gè)人陋見(jiàn):
public class Student
{
// 這里用“字段”,其實(shí)應(yīng)當(dāng)是屬性。
public string Name;
public int Age;
//自定義類(lèi) Classroom
public Classroom Class;
}
淺拷貝:Student A 淺拷貝出 Student B,Name和Age擁有新的內(nèi)存地址,但引用了同一個(gè) Classroom。
深拷貝:Student A 淺拷貝出 Student B,Name和Age擁有新的內(nèi)存地址,并且A.Classroom 的內(nèi)存地址不等于 B.Classroom。
其實(shí)俗點(diǎn)講,有點(diǎn)像:
public object Clone()
{
Student B = new Student();
B.Name = this.Name;
B.Age = this.Age;
//淺拷貝
B.Class = this.Class;
//深拷貝
B.Class = new Classromm();
B.Class.Name = this.Class.Name;
B.Class.Teacher = this.Class.Teacher;
//根據(jù)情況,對(duì)Teacher 進(jìn)行判定要進(jìn)行的是深拷貝,還是淺拷貝。
}
淺拷貝:給對(duì)象拷貝一份新的對(duì)象。
淺拷貝的定義 —— 只對(duì)值類(lèi)型(或string)類(lèi)型分配新的內(nèi)存地址。
深拷貝:給對(duì)象拷貝一份全新的對(duì)象。
深拷貝的定義 —— 對(duì)值類(lèi)型分配新的內(nèi)存地址,引用類(lèi)型、以及引用類(lèi)型的內(nèi)部字段分配的新的地址。
我是這么定義的:淺拷貝,換湯不換藥。
注意:
1、在 .NET 程序中,應(yīng)該避免使用 ICloneable 接口。
因?yàn)橥ㄟ^(guò)該接口無(wú)法判斷究竟是淺拷貝還是深拷貝,這會(huì)造成誤解或誤用。
2、深拷貝應(yīng)該復(fù)制該對(duì)象本身及通過(guò)該對(duì)象所能到達(dá)的完整的對(duì)象圖,淺拷貝只復(fù)制對(duì)象本身(就是該對(duì)象所表示的在堆中的一塊連續(xù)地址中的內(nèi)容)。
評(píng)論