关于c#中参数传递的问题

时间:2011-01-13 09:35:32

标签: c# parameter-passing

以下示例来自Jon Skeet的文章“Parameter passing in C#”。

我的问题是:为什么变量y在第一个示例中是 NOT null,而我们在第二个示例中看到它已被更改:

1 -

void Foo (StringBuilder x)
{
    x = null;
}

...

StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (y);
Console.WriteLine (y==null);

2 -

void Foo (StringBuilder x)
{
    x.Append (" world");
}

...

StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (y);
Console.WriteLine (y);

谢谢

9 个答案:

答案 0 :(得分:9)

第一个示例中

y null,正是因为参数是按值传递的。计算参数表达式(只是y),并将其值(对StringBuilder的引用复制到参数变量(x)中作为初始值。

更改x的值而不是会更改y的值。

第二个示例不会更改参数(x)的值 - 它会更改x引用的对象中的数据。 y的值仍然没有改变:它仍然引用相同的StringBuilder对象,只是该对象现在包含不同的数据。

如果我给你一张带有我家庭住址的纸张,然后你去那个地址并把房子涂成红色,你就不会改变那张纸,是吗?如果你做了在纸上划掉我的地址(如第一个例子中那样),这不会改变我的我的地址的想法 - 就像改变价值一样x的{​​{1}}不会改变y的值。

答案 1 :(得分:3)

在两个示例中,对StringBuilder的引用都是按值传递的 将y视为实际StringBuilder地址的持有者 地址本身被复制到Foo作为堆栈参数,因此第一个示例中的赋值x = null更改了复制的地址,而不是y中存储的实际地址。 在第二个示例中,x.Append指的是y指向并更改它的相同实例,因此更改是可见的。

答案 2 :(得分:2)

啊,这更有意义......这是因为指针如何工作。在这两个示例中,您都将指针的副本传递给函数。想象一下,y是一张纸上写着“左边的第二扇门”,这是我们想要存放字符串的房间。

现在,在第一个例子中,你正在制作那张纸的副本,并且该方法正在使用该副本并删除它所说的内容。由于它只是纸张的副本,原始作品不受影响。

所以现在,当我们回来并想要打印y的内容时,我们按照说明操作,找到“左边的第二扇门”,打开它并发现只有“你好”

在第二个例子中,我们仍然传递了论文的副本,但现在我们需要进行交互,所以我们按照说明进入“左边的第二扇门”,在这里我们附加字符串“world ”。所以现在,当我们回来并想要打印y的内容时,我们按照原始纸上的说明,找到“左边的第二扇门”,打开它并找到“你好世界”。 / p>

如果我们使用了ref - 关键字,我们就不会通过该论文的副本,而是将实际的论文本身传递给该方法。因此,当该方法删除写在纸上的内容时,我们现在回来时没有找到存储字符串的门的指示。

我知道,它有点幼稚的解释,但它通常在试图解释指针时起作用。

答案 3 :(得分:2)

正如Itay 已经提到的那样,对StringBuilder的引用正在通过值传递

当你致电Foo(y)时,它会复制y的值,这是数据(值)所在的内存地址,所以在x的{​​{1}}中你都有副本y变量所指向的地址。请注意,x未引用y它只获取y引用类型变量值的副本。因此,通过为x分配null, 只需将引用 从x切换到某个地址(在完整的情况下存储文本,StringBuilder数据)。由于x只获取y变量的副本而未引用它y,因此不会更改y。 在第二个示例中,您只是在y引用的地址中操作数据,这就是在更新方法调用void Foo (StringBuilder x) // x gets copy of the address to where y is referencing { // x now points to null // Remember x and y variables are located in different memory addresses and x is // not referencing y, thus only x is updated. x = null; } ... 之后的原因。

让我在两个案例中添加评论

1 -

void Foo (StringBuilder x) // x gets copy of the address to where y is referencing 
{
    // x updates the data located in the memory address to which y is referencing
    // x still points to the same address as y 
    x.Append (" world");
}
...

2 -

void Foo (ref StringBuilder x) // x points to y
{
    x = null;
}

试试这个,它会很清楚:

{{1}}

答案 4 :(得分:0)

编辑:当我回答问题时,它表示Y为空。这个问题后来改变了。

我认为你写错了代码。我已经更新了你的代码 - 请注意,Foo参数现在通过“reference”而不是“value”传递。

class Program
{
    static void Foo(ref StringBuilder x)
    {
        x = null;
    }

    static void Main(string[] args)
    {
        StringBuilder y = new StringBuilder();
        y.Append("hello");
        Foo(ref y);
        Console.WriteLine(y == null); //Prints "true".
    }
}

答案 5 :(得分:0)

对于(1)打印出true,代码必须如下所示:

void Foo (ref StringBuilder x)
{
    x = null;
}

...

StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (ref y);
Console.WriteLine (y==null);

马里奥

答案 6 :(得分:-1)

我不知道你是否熟悉指针,但它会帮助你更好地理解它。因为参数表示指向对象的指针,所以如果将指针指定给另一个对象,则会改变指针指向的位置,因此您不会修改原始对象。

在第二个示例中,您正在调用原始指针对象的方法,因此原始对象会被修改。

Java是故意这样做的,因此对于如何分配变量没有任何歧义。如果要将变量分配给方法中定义的对象,则必须返回要分配给变量的对象(例如StringBuilder builder = createStringBuilder();

答案 7 :(得分:-2)

因为在第一个例子中,他将Y传递给Food,将Y设置为null:

Foo (y);
....


void Foo (StringBuilder x) 
{     
     x = null; 
}

答案 8 :(得分:-4)

所以我想我会纠正我的答案,所以没有人读过旧答案,因为它错了。我对这个问题感到有些困惑。所以答案是:

当您传入对象的对象引用时,您可以访问该对象的属性和方法。但是,如果您将输入参数更改为其他对象,则需要更改对象,因为您正在更改变量引用的内容。希望这个例子能更好地解释它,否则请阅读Jon Skeet的回答。

public static void DoSomething(MyObject x)
{
    x.MyProperty = 2;
    x = new MyObject(); // now we lost our reference to original object
    x.MyProperty = 3; // The original object is NOT updated.
}

public static void Main(string[] args)
{
    var y = MyObject();
    y.MyProperty = 1;
    DoSomething(y);
    Console.WriteLine(y.MyProperty); // Will output "2"
}