2017-07-17 75 views
8

我正在學習C#深層複製和淺拷貝。在demo_obj1中的更改​​之後,對象值發生了更改,但列表未更新,但在demo_obj2中更改了對象值並更新了列表值。任何人都知道這裏發生了什麼? 感謝Clone中的奇怪結果()

的Visual Studio 2017年

Net框架4.6

public class Demo : ICloneable 
{ 
    public int Value { get; set; } 

    public string UID { get; set; } 

    public Demo(int nValue) 
    { 
     Value = nValue; 
    } 

    public object Clone() 
    { 
     return this.MemberwiseClone(); 
    } 
} 

public class Program 
{ 
    public static void Print(List<Demo> objList) 
    { 
     Console.WriteLine(); 
     foreach (Demo objDemo in objList) 
     { 
      Console.WriteLine("{0} = {1}", objDemo.UID, objDemo.Value); 
     } 
    } 
    public static void Main() 
    { 
     List<Demo> objList = new List<Demo>(); 

     Demo obj1 = new Demo(100); 
     obj1.UID = "Demo_obj1"; 

     Demo obj2 = (Demo)obj1.Clone(); 
     obj2.UID = "Demo_obj2"; 

     objList.Add(obj1); 
     objList.Add(obj2); 

     Print(objList); 

     obj1 = obj2; 
     obj1.Value = 200; 

     Console.WriteLine(); 
     Console.WriteLine(obj1.UID + " = " + obj1.Value); 
     Console.WriteLine(obj2.UID + " = " + obj2.Value); 

     Print(objList); 
     Console.ReadKey(); 
    } 
} 

輸出:

Demo_obj1 = 100 
Demo_obj2 = 100 

Demo_obj2 = 200 
Demo_obj2 = 200 

Demo_obj1 = 100 
Demo_obj2 = 200 
+0

@VladimirArustamian他在他的代碼中使用MemberwiseClone已經 –

+0

一個更具描述性的標題將是適當的。 – user1803551

回答

6

它與克隆無關,它是引用問題。

您已創建兩個對象obj1obj2並將它們放入列表中。
現在,您通過集合進行迭代,輸出並獲得預期結果。

引用是現在以下幾點:

obj1, list[0] -> Demo_obj1 (100) 
obj2, list[1] -> Demo_obj2 (100) 

Output list[0] => Demo_obj1 (100) 
Output list[1] => Demo_obj2 (100)  

後來,obj1 = obj2,已分配的obj2參考,obj1。您不會更改其值或複製對象,只需複製參考並將其指向另一個對象即可。
所以,實際上,他們現在都指向同一個對象。
列表包含對不同對象的相同的兩個引用。

list[0]    -> Demo_obj1 (100) 
obj1, obj2, list[1] -> Demo_obj2 (100) 

然後你讓obj2.Value = 200真正改變其值設置爲200:

list[0]    -> Demo_obj1 (100) 
obj1, obj2, list[1] -> Demo_obj2 (200) 

當您嘗試輸出obj1obj2 UID和值現在,你將在同一對象的實際輸出值(Demo_obj2 )。

Output obj1 => Demo_obj2 (200) 
Output obj2 => Demo_obj2 (200) 

但是,如果你試圖通過收集迭代,你會得到Demo_obj1Demo_obj2再次,根據參考表。

Output list[0] => Demo_obj1 (100) 
Ouptut list[1] => Demo_obj2 (200) 
3

您已設置變量obj1是在obj2舉行的實例。列表中仍然存在obj1的原始值。在你所在的線上obj1 = obj2;這是設置變量obj1以保存obj2。它不會替換之前存儲在obj1中的實例的值。這就是爲什麼你看到你描述的輸出。

5

所以我認爲有幾點你需要從這個問題來理解。

首先關於您的實際問題,clone()方法確實會給您2個對象。它們都以值100開頭,並添加到列表中。請注意,此列表指向obj1obj2中包含的對象,並且不使用您已創建的引用。

然後你這樣做:

obj1 = obj2; 
obj1.Value = 200; 

這樣做是你參考obj1obj2所以現在他們都指向同一個對象的更新。你可以看到這一點,當你正在記錄,你看到200兩次。請注意,你有不是更新你列表中的指針,它們是完全不同的指針。

最後,當你運行你的第二個日誌,使用你看到原來obj1與價值100,哪些是你更新到有200值第二obj2在列表中的指針。


現在,這裏的有趣的事情,是這實際上不是一個深克隆的一個很好的例子,因爲你使用會一下就無論如何都會被複制的原始值。爲了實現你可能想在對象上包裹一些值一個更好的結果:

class Bar 
{ 
    public int Value; 
    public Bar(int value) 
    { 
     this.value = value; 
    } 
}  

class Foo : ICloneable 
{ 
    public String Id; 
    public Bar MyBar; 

    public Foo(int value) { 
     this.MyBar = new Bar(value); 
    } 

    public object Clone() 
    { 
     return this.MemberwiseClone(); 
    } 
} 

現在,如果你要建立一個酒吧,並做了Foo的淺克隆現在,它仍然會使用相同的Bar。所以:

Foo f = new Foo(100); 
Foo f2 = (Foo)f.Clone(); 

f2.MyBar.Value = 200; 

Console.WriteLine(f.MyBar.Value); // 200 
Console.WriteLine(f2.MyBar.Value); // 200 

這就是你需要進入深克隆,這樣每個Foo實例使用不同的參照獨特Bar

+0

感謝您的回答 – QweRty

+0

關於可克隆對象內的引用類型的好處 – Fredou

3

忘掉MemberwiseClone,它是一個紅鯡魚在這裏。

你的問題就在這裏:

obj1 = obj2; 
obj1.Value = 200; 
  1. 變量是佔位符。
  2. 存儲在引用類型變量中的值是其引用「存儲」在內存中的對象的「地址」。
  3. 默認情況下,變量是通過C#中的值複製的。

因此,這意味着obj1 = obj2; obj1.Value = 200;執行以下操作:。

  1. 獲取(存儲在obj2的值,其中實例{ "Demo_obj2"; 100 }生活
  2. 複製該值裝入德變量obj1地址現在obj1的值是同一個實例{ "Demo_obj2"; 100 }所在的地址; obj1obj2指向相同對象
  3. obj1(或obj2)引用的對象的Value更改爲200:結果爲實例{ "Demo_obj2"; 200 }
  4. 以前obj1引用的實例{ "Demo_obj1"; 100 }沒有被任何你的兩個變量obj1obj2的了引用,但實例仍保存(你沒有碰過它!)列表,並通過objList[0]到達。

你現在明白你爲什麼會得到你所看到的行爲嗎?