2

我遇到了这个棘手的问题。请参阅以下代码:

public class Foo
    public int A { get; set; }
}

static void Main(string[] args)
{
    Foo foo1 = new Foo();
    Foo foo2 = new Foo();

    ChangeObject1(foo1);
    ChangeObject2(ref foo2);

    Console.WriteLine("Foo1 = " + foo1.A);
    Console.WriteLine("Foo2 = " + foo2.A);
    Console.ReadKey();
}

static void ChangeObject1(Foo foo)
{
    foo.A += 1;
    foo = new Foo();
}

static void ChangeObject2(ref Foo foo)
{
    foo.A += 1;
    foo = new Foo();
}

经过一番测试,foo1.A 等于 1,而 foo2.A 仍然等于 0。据我了解,这是因为第二种方法是通过引用而不是值传递的实例。这会更改实例的内容,因此当在方法中创建和分配新对象时,不再定义值。

但是想一想我就迷惑了,因为我觉得第一种方法应该有类似的效果,即使它是按值传递的,因为实例的内容仍然在方法之外发生了变化。

有谁知道实际发生了什么?谢谢!

4

2 回答 2

3

我假设您使用的是 c# 或 java。无论哪种方式,这个概念都是一样的

当你声明你的 foo 变量时

Foo foo1 = new Foo()

您正在声明所谓的引用类型。(所有代表类的对象都是引用类型)

所以,存储在其中的值foo1并不是真正的 a Foo,而是一个保存 that 的内存地址的值Foo

你打电话时

    static void ChangeObject1(Foo foo)
    {
        foo.A += 1;
        foo = new Foo();
    }

您正在传递该参考的副本。因此,当您这样做时foo.A += 1,您正在更改刚刚传入的对象的成员值。

当你这样做时,foo = new Foo(); 你只是改变了引用的副本,而不是原始对象引用本身。

现在,当你打电话时

    static void ChangeObject2(ref Foo foo)
    {
        foo.A += 1;
        foo = new Foo();
    }

您没有传递您的地址的副本,Foo而是传递了您的地址的地址Foo

你可以把它想象成你正在传递原始Foo地址本身。

因此,当您致电foo = new Foo()foo 之前,您对 foo 做了什么并不重要。您刚刚用一个全新Foo对象的地址覆盖了您的地址。

如果您将方法更改为如下所示

    static void ChangeObject2(ref Foo foo)
    {
        foo.A += 1;
    }

那么你应该注意到变化

这是一个解释引用类型的页面 它在java中,但同样的原则也适用于c#

于 2012-09-11T18:31:44.577 回答
-1

也许如果您从值类型而不是引用类型的角度考虑它会有所帮助。以第一个例子为例(括号中的数字代表一个包含整数值的内存地址)。

按值传递

[0001] -> 5;

当你按值传递时,值 5 被复制到一个新的内存地址

[0002] -> 5;    
[0002] -> 10;    // changes the value of a new reference

更改 [0002] 的值不会影响 [0001]。


通过引用传递

[0001] -> 5;

另一方面,如果您传递地址本身(使用 ref 关键字传递)

[0001] -> 5;    
[0001] -> 10;    // changes the value of the original reference

您现在可以更改引用所持有的值。


引用类型而不是保存整数,而是保存对象的地址。因此,如果您通过引用传递,您将传递保存该值的“容器”(即指针/引用)。然后,您可以更改容器中的内容。如果按值传递,就是将值从容器中取出并传递,那么原来的容器就出局了。

于 2012-09-12T05:34:54.067 回答