开发者

A question about Parameter passing in c#

The below example is got from Jon Skeet's article, "Parameter passing in C#".

My question is: Why variable y is NOT null in the first example while we see that it has been changed in the second example:

1-

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

...

StringBuilder y = new StringBuil开发者_运维知识库der();
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);

Thank you


y is not null in the first example, precisely because the argument is passed by value. The argument expression (simply y) is evaluated, and its value (a reference to a StringBuilder is copied into the parameter variable (x) as the initial value.

Changing the value of x does not change the value of y.

The second example doesn't change the value of the parameter (x) - it changes the data within the object that x refers to. The value of y still hasn't changed: it still refers to the same StringBuilder object, it's just that that object now contains different data.

If I give you a piece of paper with my home address on, and you go to that address and paint the house red, you're not changing anything about the piece of paper, are you? And if you did cross out my address on the piece of paper (as in the first example) that wouldn't change my idea of my address - just as changing the value of x doesn't change the value of y.


In both examples the reference to the StringBuilder is being passed by value.
Think of y as the holder of the address of the actual StringBuilder.
The address itself is being copied to Foo's as stack parameter so the assignment x = null in the first example changes the copied address and not the actual address stored in y.
On the second example x.Append refers to the same instance that y points to and changes it, hence the change is visible.


Ah, that makes more sense... it is because how pointer work. In both examples you are passing a copy of a pointer to the function. Imagine y being a piece of paper saying "second door on the left", this is the room where we want to store the string.

Now, in the first example you're making a copy of that piece of paper and the method is taking that copy and erasing what it says. Since its just a copy of the paper the original piece is not affected.

So now, when we're back and we want to print the content of y, we follow the instructions, find the "second door on the left", open it and finds only "hello"

In the second example, we're still passing a copy of the paper, but now we need to interact so we follow the instructions to get to the "second door on the left" and in here we append the string " world". So now, when we're back and we want to print the content of y, we follow the instructions written on our original piece of paper, find the "second door on the left", open it and finds "hello world".

If we had used the ref-keyword, we wouldn't have passed a copy of the paper, but the actual paper itself to the method. So when the method erases what is written on the paper, we are now back with no directions to find the door where our string is stored.

I know, its a bit childish explanation but it usually works when trying to explain pointers.


As already mentioned by Itay "the reference to the StringBuilder is being passed by value".

When you call Foo(y) it copies the value of y which is the address of the memory where the data(value) is located so in both cases in x you have the copy of the address to where y variable is pointing. Note that x is not referencing y it just gets the copy of the y reference type variable value. So by assigning null to x you just cut the reference from x to some address (which in out case stores text, StringBuilder data). And as x just gets copy of the y variable and not referencing it y is not changed. In the second example you are just manipulating data in address to which y is referencing that is why after method call y is updated.

Let me add comments on both cases

1-

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;
}
...

2-

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");
}
...

Try this, it will clear up much:

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


EDIT: When I answered the question, it stated that Y was null. The question has since been changed.

I think you've written the code wrong. I've updated your code - note that the Foo argument is now passed by "reference" and not by "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".
    }
}


For (1) to print out true, the code would have to look like this:

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

...

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

Mario


I don't know if you're familiar with pointers, but it'll help you understand this better. Because parameters represent pointers to objects, if you assign the pointer to another object, you're changing where the pointer points to therefore you're not modifying the original object.

In the second example, you're calling a method of the object of the original pointer, therefore the original object gets modified.

Java did it this way on purpose so that there would be no ambiguity as to how assigning variables works. If you want to assign a variable to an object defined in a method, you must return that object to be assigned to the variable (StringBuilder builder = createStringBuilder(); for instance)


Becuase in the first example he passes Y to Foo which sets Y to null:

Foo (y);
....


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


So I figured I'll correct my answer so no one read the old one since it was wrong. I was a little bit confused by the question. So the answer is:

When you are passing in an object reference to a method you have access to that objects properties and methods. But if you change your input parameter to something else the object want be changed since your are changing what the variable is referencing to. Hopefully this example explains it better, otherwise read Jon Skeet's answer.

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"
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜