2010-07-01 57 views
4

我认为如果一个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()); 

    } 

回答

3

当您设置:

array[0] = foo; 

你实际上在做参照对象的按值拷贝由富指出,并引用到复制“数组[0]”。

后来,当你这样做:

foo = bar; 

你正在做同样的事情 - 复制,按值,将参考指向的对象吧,到变量foo中。这对数组[0]没有影响,因为引用最初是通过值复制的。为了使第一个DerivedClass1实例(foo的原始引用)成为GC的候选者,您需要显式地将array [0]设置为某个其他引用(或null)。

1

您没有正确地考虑引用分配。 array[0] = foo;导致存储器位置array[0]保存对存储器位置foo所引用的同一对象的引用。它不会导致array[0]引用存储器位置foo本身。因此,更改哪个对象foo引用不会影响array[0]引用的对象。

0

你有两个引用到DerrivedClass1实例:

  • 当地foo变量,虽然foo停止尽快与所述第一对象的引用为您分配bar
  • 数组项在array[0] 。直到方法结束时,此引用保持不变。

分配到foo变量都不会影响到阵列中的内容 - 只有在更改array[0]将这样做。

该对象在清除所有引用之前不符合垃圾收集条件。这意味着在数组被垃圾收集之前,DerrivedClass1对象不会被垃圾收集。

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 = new Foo()(或者你说foo = bar),你正在改变foo来引用另一个对象。

您的数组元素不知道或关心foo,它只知道foo在赋值时引用的对象。

0

您创建一个新的DerivedClass1对象并将其引用存储在foo中,然后将该引用复制到数组[0]中,将其复制,即在此时您有两个对同一对象的引用。

当你做foo = bar;时,你会覆盖从foo变量引用的第一个副本,而不是引用本身,所以现在foo只是指向另一个对象,它与bar指向的相同,但数组[0]仍然指向第一个,所以它不应该被垃圾收集,并且对象不变。

3

您更改了变量foo的引用,而不是array[0]

在你的想法中,你认为array[0]指向foo,他们实际上都指向堆中的相同内存位置。

当您将bar指定为foo时,您正在更改foo指向的位置。您尚未触及array[0],因此它仍指向它始终指向的相同内存位置 - 原始foo

+0

此外,这与垃圾收集无关。我建议抓取Jeffrey Richter的C#Via C#的副本。这本书适合你的关卡。 – Will 2010-07-01 17:15:42

+0

感谢这本书,我现在正在阅读它。 – 2010-07-01 19:12:18

1

foo只引用存储在内存中的对象。

array[0] = foo会将引用复制到数组,因此数组和foo现在都指向相同的对象。

foo = barbar参考复制到foo,这样既foobar点到第二对象。但数组仍将指向第一个对象,因为您没有更改数组中的引用。

您应该将其视为家庭住址。如果你的两个朋友知道你的旧地址,但你只告诉朋友A你的新地址,朋友B将访问你的旧地址并找到你的旧房子。在你的例子中,数组是朋友B,只知道第一个对象的内存位置。