数组持有对灭绝对象的引用

时间:2010-07-01 16:44:58

标签: c# arrays abstract-class pass-by-reference

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

    }

7 个答案:

答案 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,因此foobar都指向第二个对象。但是数组仍将指向第一个对象,因为您没有更改数组中的引用。

您应该将其视为家庭住址。如果您的两个朋友知道您的旧地址,但您只告诉朋友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]仍然指向第一个,所以它不应该被垃圾收集,并且对象不变。