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