我正在学习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
答案 0 :(得分:5)
它与克隆无关,它是引用问题。
您已创建了两个对象obj1
和obj2
并将它们放入列表中
现在,您遍历集合,输出并获得预期结果。
现在引用如下:
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)
当您尝试现在输出obj1
和obj2
UID和值时,您将实际输出同一对象(Demo_obj2)的值。
Output obj1 => Demo_obj2 (200)
Output obj2 => Demo_obj2 (200)
但是,如果您尝试遍历集合,则会根据参考表再次获得Demo_obj1
和Demo_obj2
。
Output list[0] => Demo_obj1 (100)
Ouptut list[1] => Demo_obj2 (200)
答案 1 :(得分:4)
所以我认为你需要从这个问题中理解几点。
首先,关于您的实际问题,clone()
方法确实为您提供了2个对象。它们都以值100
开始并添加到列表中。请注意,此列表指向obj1
和obj2
中包含的对象,并且不使用您已创建的引用。
然后你这样做:
obj1 = obj2;
obj1.Value = 200;
这样做会更新您引用obj1
到obj2
,所以现在它们都指向同一个对象。当您进行日志记录并且看到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;
因此,这意味着obj1 = obj2; obj1.Value = 200;
执行以下操作:
obj2
中的值(实例{ "Demo_obj2"; 100 }
所在的地址。obj1
。现在obj1
的值是同一个实例{ "Demo_obj2"; 100 }
所在的地址; obj1
和obj2
都指向同一个对象。Value
(或obj1
)引用的对象的obj2
更改为200
:结果为实例{ "Demo_obj2"; 200 }
。{ "Demo_obj1"; 100 }
引用的实例obj1
不再被您的两个变量obj1
或obj2
中的任何一个引用,但实例仍然存储(你没有碰到它!)在列表中,可通过objList[0]
。你现在明白为什么你会得到你所看到的行为吗?