問題起源:
昨天被同事問到一個淺拷貝與深拷貝區別的問題,說實在的,記得在學校時在書在看過相關概念區別。
只是,那時的在校生,又有幾個能對書本上那寫的盡量讓鬼都看不懂知識能清晰的理解呢。
工作後雖然也有用到Clone相關的內容,不過也僅是應用,基礎的概念還是沒去仔細的理解,以於後來DataTable內部那不深不淺的架構拷貝都混和這些概念混在一起了。
曾經的以為:
讓得一年多以前,我去面試,被一個人問到淺拷貝與深拷貝區別,我答:淺拷貝就是拷貝就是拷貝架構,不包括值,深拷貝就是連值也一同拷貝。
當我答出這樣錯誤的答案時,對方沒有糾正,反而似乎對我的答案還挺滿意的。
唉,面試管自己不懂還問我這問題,還讓我一直以為到現在都是這個樣。
同事的質問:
接著同事說它的樣本裡,發現值是有拷貝的,於是我意識到我的認知是有問題了,我重新百度了一下。
網上亂七雜八的答案,讓我得一個模糊的似乎正確的答案:淺拷貝不拷貝內部類的對象
引申出的錯亂:
接著同事的疑問更一度把我引向這個容易迷亂的錯誤深淵:淺拷貝是對參考型別拷貝地址,對實值型別直接進行拷貝。
最後,為瞭解惑,還是用樣本來說話了:
class DemoClass : ICloneable
{
public int intValue = 1;
public string strValue = "1";
public PersonEnum pEnum = PersonEnum.EnumA;
public PersonStruct pStruct = new PersonStruct();
public Person pClass = new Person("1"); public int[] pIntArray = new int[] { 1 };
public string[] pArray = new string[] { "1" };
#region ICloneable 成員
public DemoClass()
{
pStruct.StructValue = 1;
}
public object Clone()
{
return this.MemberwiseClone();
}
#endregion
}
class Person
{
public string Name;
public Person(string name)
{
Name = name;
}
}
public enum PersonEnum
{
EnumA=1,
EnumB=2
}
public struct PersonStruct
{
public int StructValue;
}
說明:
這裡的樣本,我用上了:
int
stringint[]
string[]
enum
struct
class
然後接下來會產生執行個體A和複製執行個體B。
接著改變B的值,看A的值會不會被改變。
接下我們看main方法
static void Main(string[] args)
{
Demo();
}
public static void Demo()
{
DemoClass A = new DemoClass();
DemoClass B = (DemoClass)A.Clone();
B.intValue = 2;
Write(string.Format(" int->[A:{0}] [B:{1}]", A.intValue, B.intValue));
B.strValue = "2";
Write(string.Format(" string->[A:{0}] [B:{1}]", A.strValue, B.strValue));
B.pEnum = PersonEnum.EnumB;
Write(string.Format(" Enum->[A:{0}] [B:{1}]", (int)A.pEnum, (int)B.pEnum));
B.pStruct.StructValue = 2;
Write(string.Format(" struct->[A:{0}] [B:{1}]", A.pStruct.StructValue, B.pStruct.StructValue));
B.pIntArray[0] = 2;
Write(string.Format(" intArray->[A:{0}] [B:{1}]", A.pIntArray[0], B.pIntArray[0]));
B.pStringArray[0] = "2";
Write(string.Format("stringArray->[A:{0}] [B:{1}]", A.pStringArray[0], B.pStringArray[0]));
B.pClass.Name = "2";
Write(string.Format(" Class->[A:{0}] [B:{1}]", A.pClass.Name, B.pClass.Name));
System.Console.Read();
}
static void Write(string msg)
{
System.Console.WriteLine(msg);
}
說明:
我們通過改變B執行個體的值,然後列印出A和B的值看結果。
列印結果如下:
從最後輸出的結果我們得知:
對於內部的Class的對象和數組,會Copy地址一份。[從而改變B時,A也被改變了]
而對於其它內建的int/string/Enum/struct/object類型,則進行值copy。
最後,我試著百度尋找淺拷貝最原生的定義,淺搜了一下找不到,那這個定義,就留給讀者解答一下了。
接著,我順手比較了一下淺拷貝和深拷貝的效能測試:
先在DemoClass加入一個函數:
public object CloneNew()
{
return new DemoClass();
}
接著我們寫範例程式碼比較:
public static void Compare()
{
DemoClass baseClass = new DemoClass();
DateTime start = DateTime.Now;
for (int i = 0; i < 1000000; i++)
{
DemoClass newClass = (DemoClass)baseClass.Clone();
}
TimeSpan ts = DateTime.Now - start;
System.Console.WriteLine("淺拷貝:" + ts.Ticks);
DateTime start2 = DateTime.Now;
for (int j = 0; j < 1000000; j++)
{
DemoClass newClass = (DemoClass)baseClass.CloneNew();
}
TimeSpan ts2 = DateTime.Now - start2;
System.Console.WriteLine("深拷貝:" + ts2.Ticks);
System.Console.Read();
}
最後得出結果:
看來直接用淺拷貝效能還不如直接返回一個new的對象。同樣的,用複製返回object最後還得對類型轉換進行一些開銷,教人情何以堪!
附言:
這兩天剛好感冒,插了這兩篇解惑篇,都是臨時插進來的的問題了,接下來會繼續寫 CYQ.Data 輕量資料層之路 架構 的相關文章。