我认为如果C#数组持有引用类型,它只会保存对每个对象的引用,但下面的代码告诉我。看起来好像数组正在保存对我认为被设置为垃圾收集的对象的引用。我觉得我在这里缺少一些基本的东西。谁能告诉我为什么在重新分配foo时数组引用不会改变?
abstract class AbstractBaseClass
{
protected int _someProperty;
public virtual int SomeProperty
{
get
{
return _someProperty;
}
set
{
_someProperty = value;
}
}
}
class DerrivedClass1 : AbstractBaseClass
{
}
class DerrivedClass2 : AbstractBaseClass
{
public override int SomeProperty
{
get
{
return _someProperty + 1;
}
set
{
_someProperty = value;
}
}
}
static void Main()
{
AbstractBaseClass foo;
AbstractBaseClass bar;
AbstractBaseClass[] array = new AbstractBaseClass [1];
foo = new DerrivedClass1();
foo.SomeProperty = 99;
array[0] = foo;
Console.WriteLine("Value of foo.SomeProperty: " + foo.SomeProperty.ToString());
bar = new DerrivedClass2();
bar.SomeProperty = 99;
Console.WriteLine("Value of bar.SomeProperty: " + bar.SomeProperty.ToString());
foo = bar;
Console.WriteLine("Value of foo.SomeProperty after assignment: " + foo.SomeProperty.ToString());
Console.WriteLine("Value of array[0] after assignment: " + array[0].SomeProperty.ToString());
}
答案 0 :(得分:3)
设置时:
array[0] = foo;
你实际上正在对foo指向的对象的引用进行副值复制,并将该引用复制到“array [0]”。
稍后,当你这样做时:
foo = bar;
你正在做同样的事情 - 将值的值复制到变量foo中,这是对bar指向的对象的引用。这对数组[0]有无效,因为引用最初是按值复制的。为了使第一个DerivedClass1
实例(foo的原始引用)成为GC的候选者,您需要将array [0]显式设置为其他引用(或null)。
答案 1 :(得分:3)
您更改了变量foo
的引用,而不是array[0]
。
在您看来,您认为array[0]
指向foo
,它们实际上都指向堆内的相同内存位置。
当您将bar
分配给foo
时,您正在更改foo
分的位置。您尚未触及array[0]
,因此它仍然指向它始终指向的相同内存位置 - 原始foo
。
答案 2 :(得分:1)
您没有正确考虑参考分配。 array[0] = foo;
导致内存位置array[0]
保存对内存位置foo
引用的同一对象的引用。它不会导致array[0]
引用内存位置foo
本身。因此,更改哪个对象foo
引用不会影响哪个对象array[0]
引用。
答案 3 :(得分:1)
数组元素存储对foo最初引用的对象的引用。 数组元素没有直接绑定到foo。当您修改对象foo引用时,数组元素不受影响。
Foo foo = new Foo();
foo.Name = "Andy";
Foo anotherFoo = foo;
foo.Name = "Bart";
Console.WriteLine(anotherFoo.Name); // writes Bart
foo = new Foo();
foo.Name = "Claire";
Console.WriteLine(anotherFoo.Name); // writes Bart
当你修改foo引用的对象的属性时,引用同一个对象的其他变量将“看到”那些更新,因为它们正在查看同一个对象。但是,当我说foo = new Foo()
(或您说foo = bar
)时,您正在更改foo以引用另一个对象。
你的数组元素不知道或不关心foo,它只知道foo在赋值时引用的对象。
答案 4 :(得分:1)
foo
仅引用存储在内存中的对象。
array[0] = foo
会复制对数组的引用,因此数组和foo
现在都指向同一个对象。
foo = bar
会将bar
引用复制到foo
,因此foo
和bar
都指向第二个对象。但是数组仍将指向第一个对象,因为您没有更改数组中的引用。
您应该将其视为家庭住址。如果您的两个朋友知道您的旧地址,但您只告诉朋友A您的新地址,朋友B将访问您的旧地址并找到您的旧房子。在您的示例中,数组是朋友B,只知道第一个对象的内存位置。
答案 5 :(得分:0)
您有两个对DerrivedClass1
实例的引用:
foo
变量,虽然foo
只要您将bar
分配给第一个对象,就会停止对其进行引用array[0]
处的数组项。在方法结束之前,此引用保持不变。 foo
变量的赋值永远不会影响数组的内容 - 只需对array[0]
进行更改就可以了。
在清除对它的所有引用之前,该对象不符合垃圾回收的条件。这意味着在对数组进行垃圾回收之前,不会对DerrivedClass1
对象进行垃圾回收。
答案 6 :(得分:0)
您创建一个新的DerivedClass1对象并将其引用存储在foo中,然后将该引用复制到array [0],复制它,也就是说,此时您有两个对同一对象的引用。
当你执行foo = bar;
时,你会覆盖foo变量的第一个引用副本,而不是引用本身,所以现在foo只指向另一个对象,就像bar指向的那样,但是array [0]仍然指向第一个,所以它不应该被垃圾收集,并且对象不变。