2017-07-17 24 views
8

Tôi đang học C# Deep Copy và Shallow Copy. Ở đây sau khi thay đổi trong demo_obj1, giá trị đối tượng được thay đổi nhưng danh sách không được cập nhật nhưng trong giá trị đối tượng demo_obj2 được thay đổi và cũng có giá trị danh sách được cập nhật. Có ai biết chuyện gì đang xảy ra ở đây không? Cảm ơnkết quả lạ trong Clone()

Visual studio 2017

.Net framework 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(); 
    } 
} 

Output:

Demo_obj1 = 100 
Demo_obj2 = 100 

Demo_obj2 = 200 
Demo_obj2 = 200 

Demo_obj1 = 100 
Demo_obj2 = 200 
+0

@VladimirArustamian anh ấy sử dụng MemberwiseClone trong mã của anh ấy đã là –

+0

Một tiêu đề mô tả hơn sẽ phù hợp. – user1803551

Trả lời

6

Không có gì liên quan đến nhân bản, đó là vấn đề tham chiếu.

Bạn đã tạo hai đối tượng obj1obj2 và đặt chúng vào danh sách.
Bây giờ, bạn lặp qua bộ sưu tập, xuất nó và nhận kết quả mong đợi.

Tài liệu tham khảo như sau bây giờ:

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

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

Sau đó, bởi obj1 = obj2, bạn đã gán một tham chiếu của obj2-obj1. Bạn không thay đổi giá trị của nó hoặc sao chép đối tượng của bạn, bạn chỉ cần sao chép tham chiếu và làm cho nó trỏ đến một đối tượng khác.
Vì vậy, trên thực tế, cả hai hiện tại đều trỏ đến cùng một đối tượng.
Danh sách chứa cùng hai tham chiếu đến các đối tượng khác nhau.

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

Sau đó bạn thực hiện obj2.Value = 200 thực sự thay đổi giá trị của nó đến 200:

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

Khi bạn cố gắng đầu ra obj1obj2 UID và giá trị bây giờ, bạn sẽ thực sự ra giá trị của cùng một đối tượng (Demo_obj2).

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

Tuy nhiên, nếu bạn cố gắng để lặp qua bộ sưu tập, bạn sẽ nhận được Demo_obj1Demo_obj2 một lần nữa, theo bảng tham khảo.

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

Bạn đã đặt biến obj1 là ví dụ tổ chức tại obj2. Giá trị ban đầu của obj1 vẫn tồn tại trong danh sách. trên dòng nơi bạn thực hiện obj1 = obj2; điều này là đặt biến số obj1 để giữ obj2. nó không thay thế các giá trị của cá thể đã được lưu trước đó trong obj1. đây là lý do tại sao bạn thấy kết quả đầu ra mà bạn mô tả.

5

Vì vậy, tôi nghĩ có một vài điểm bạn cần hiểu từ câu hỏi này.

Trước hết liên quan đến câu hỏi thực tế của bạn, phương pháp clone() thực sự cung cấp cho bạn 2 đối tượng. Cả hai đều bắt đầu với giá trị 100 và được thêm vào danh sách. Lưu ý rằng danh sách này trỏ đến các đối tượng chứa trong số obj1obj2 và không sử dụng các tham chiếu mà bạn đã tạo.

Sau đó, bạn làm như sau:

obj1 = obj2; 
obj1.Value = 200; 

Điều này không là bản cập nhật bạn tham khảo obj1-obj2 vì vậy bây giờ cả hai đều trỏ đến cùng một đối tượng. Bạn có thể thấy điều này khi bạn đang đăng nhập và bạn thấy 200 hai lần. Lưu ý rằng bạn có không phải đã cập nhật các con trỏ trong danh sách của bạn, chúng là các con trỏ hoàn toàn khác nhau.

Cuối cùng khi bạn chạy nhật ký thứ hai, sử dụng con trỏ trong danh sách bạn thấy obj1 gốc có giá trị 100 và số thứ hai obj2 mà bạn đã cập nhật để có giá trị 200.


Bây giờ điều thú vị ở đây, đây thực sự không phải là một ví dụ rất hay về bản sao sâu, bởi vì bạn đang sử dụng các giá trị nguyên thủy sẽ vẫn được sao chép. Để đạt được kết quả tốt hơn có thể bạn muốn quấn một số giá trị trong vòng một đối tượng:

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(); 
    } 
} 

Bây giờ nếu bạn đã xây dựng một quầy bar và làm một bản sao cạn của một Foo bây giờ, nó vẫn muốn sử dụng cùng một Bar. Vì vậy:

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 

Đây là nơi bạn sẽ cần phải nhận được vào nhân bản sâu để mỗi Foo trường hợp sử dụng một tài liệu tham khảo khác nhau để một độc đáo Bar.

+0

Cảm ơn bạn đã trả lời – QweRty

+0

điểm tốt về loại tham chiếu bên trong một đối tượng có thể khóa – Fredou

3

Hãy quên đi MemberwiseClone, một cá trích màu đỏ ở đây.

vấn đề của bạn là ở đây:

obj1 = obj2; 
obj1.Value = 200; 
  1. biến là giữ chỗ cho giá trị.
  2. Giá trị được lưu trữ trong biến được tham chiếu đã nhập là "địa chỉ" trong đó đối tượng tham chiếu "cuộc sống" trong bộ nhớ.
  3. Biến, theo mặc định, được sao chép theo giá trị trong C#.

Vì vậy, điều này có nghĩa rằng obj1 = obj2; obj1.Value = 200; nào sau đây:..

  1. Lấy giá trị được lưu trữ trong obj2 (địa chỉ nơi dụ { "Demo_obj2"; 100 } cuộc sống
  2. Copy đánh giá cao thành de biến obj1 Bây giờ obj1 Giá trị của là địa chỉ trong cùng một trường hợp { "Demo_obj2"; 100 } cuộc sống; cả hai obj1obj2trỏ đến cùng một đối tượng.
  3. Thay đổi Value của đối tượng được tham chiếu bởi obj1 (hoặc obj2) đến 200: kết quả là ví dụ { "Demo_obj2"; 200 }.
  4. Các dụ { "Demo_obj1"; 100 } trước đây được tham chiếu bởi obj1 không tham chiếu nữa bởi bất kỳ của hai biến bạn obj1 hoặc obj2, nhưng trường hợp vẫn còn được lưu trữ (bạn chưa chạm vào nó!) trong danh sách và có thể truy cập thông qua objList[0].

Bây giờ bạn có hiểu tại sao bạn đang nhận được hành vi mà bạn đang thấy không?

Các vấn đề liên quan