Clone中的奇怪结果()

时间:2017-07-17 10:32:41

标签: c#

我正在学习C#Deep Copy和Shallow Copy。在demo_obj1中更改后,对象值已更改但列表未更新但在demo_obj2中对象值已更改且列表值已更新。谁知道这里发生了什么? 感谢

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

输出:

Demo_obj1 = 100
Demo_obj2 = 100

Demo_obj2 = 200
Demo_obj2 = 200

Demo_obj1 = 100
Demo_obj2 = 200

4 个答案:

答案 0 :(得分:5)

它与克隆无关,它是引用问题。

您已创建了两个对象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包含对不同对象的相同两个引用。

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)

答案 1 :(得分:4)

所以我认为你需要从这个问题中理解几点。

首先,关于您的实际问题,clone()方法确实为您提供了2个对象。它们都以值100开始并添加到列表中。请注意,此列表指向obj1obj2中包含的对象,并且不使用您已创建的引用。

然后你这样做:

obj1 = obj2;
obj1.Value = 200;

这样做会更新您引用obj1obj2,所以现在它们都指向同一个对象。当您进行日志记录并且看到200两次时,您可以看到这一点。请注意,您已经更新了列表中的指针,它们是完全不同的指针。

最后,当您运行第二个日志时,使用列表中的指针,您会看到原始obj1的值为100,而第二个obj2您更新的值为{ {1}}。

现在有趣的是,这实际上并不是深度克隆的一个很好的例子,因为你使用的原始值无论如何都会被复制。为了获得更好的结果,您可能希望在对象中包含一些值:

200

现在,如果您要构建一个条形并立即对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

答案 2 :(得分:2)

您已将变量obj1设置为obj2中保存的实例。 obj1的原始值仍然存在于列表中。在您执行obj1 = obj2;行的位置,这是将变量obj1设置为保留obj2。它不会替换先前存储在obj1中的实例的值。这就是为什么你看到你描述的输出。

答案 3 :(得分:2)

忘掉MemberwiseClone,这是一只红鲱鱼。

您的问题在这里:

obj1 = obj2;
obj1.Value = 200;
  1. 变量是值的占位符
  2. 存储在引用类型变量中的值是“地址”,其中引用的对象“存在”内存中。
  3. 默认情况下,变量按C#中的值复制。
  4. 因此,这意味着obj1 = obj2; obj1.Value = 200;执行以下操作:

    1. 获取存储在obj2中的值(实例{ "Demo_obj2"; 100 }所在的地址。
    2. 将该值复制到de变量obj1。现在obj1的值是同一个实例{ "Demo_obj2"; 100 }所在的地址; obj1obj2 都指向同一个对象
    3. Value(或obj1)引用的对象的obj2更改为200:结果为实例{ "Demo_obj2"; 200 }
    4. 以前由{ "Demo_obj1"; 100 }引用的实例obj1不再被您的两个变量obj1obj2中的任何一个引用,但实例仍然存储(你没有碰到它!)在列表中,可通过objList[0]
    5. 到达

      你现在明白为什么你会得到你所看到的行为吗?