pass by value vs. by reference question

Z

z_learning_tester

I'm reading the MS press C# book and there seems to be a contradiction.
Please tell me which one is correct, 1 or 2.
Thanks!
Jeff

1. First it gives the code below saying that it prints 0 then 42.
They say that 42 is printed the second time since the value was wrapped in a
class and therefore became passed by reference.
(sorry for any typos I am a newbie here ;-)

class Application
{
static void Entrance()
{
WrappedInt wi = new WrappedInt();
Console.WriteLine(wi.Number); //This one prints 0
they say...
Pass.Reference(wi);
Console.WriteLine(wi.Number) //And this one
prints 42.
}
}

class WrappedInt
{
public int Number;
}

class Pass
{
public static void Reference(WrappedInt param)
{
param.Number = 42;
}
}

--------------------------------

2. Then it goes on to say that whether it is passed by value or reference,
the original arguement is unchanged unless you use 'ref' or 'out'.
So what gives? Which is correct, 1 or 2? If 2 is correct, then the example
they gave in 1 shouldn't be valid right? I may be missing something you
would find obvious.

Thanks for any explanations!
 
J

J.Marsch

The first one does print 0, and the second does print 42. And both 1 and 2
are technically correct.

Explanation:

The key to this is understanding the difference between a reference type an
a value type.

Anything that you define as a class (like the WrappedInteger class) is a
reference type. That means that in memory, it never really "lives" inside a
variable. Rather, any variables that are of type WrappedInteger, are
"references" (think "pointer" -- not exactly like a C++ pointer, but along
those lines).

So, when you set wi = new WrappedInt();

Whats really happening is that a WrappedInt() object is being created
somewhere in memory, and then a reference (kinda like a pointer) to that
object is being stored in the variable wi.

So if you do this:
WrappedInt wi = new WrappedInt();
WrappedInt wi2 = wi;

wi2 and wi are both referencing (pointing to) the same object instance. So,
if you do this:
wi.Number = 10;
w2.Number = 20;
Console.WriteLine(wi.Number)

You get "20", because wi and w2 are both pointing to exactly the same
location in memory.

Now, in contrast, lets look at a value type. your primitives like
ints/floats/bools (full list in the help) are value types, as is anything
that you define as a struct. That means that when you assign one to the
other it is copied in memory -- it really does "live" in the variable.

So:
int x = 5;

The value 5 is deposited in the memory location for x.

int y = x;
x = 10;

y is 5, and x is a 10.

the 5 is copied from x to y, and it is now a copy -- y is independent of x
so when you change x, it only affects x. This should be very familiar
ground -- its the behavior you would expect when dealing with ints in most
common languages (VB 6, Delphi, etc)

Now, lets look at their example:
when they call Pass.Reference(wi), you are passing the value in wi, which is
really just a pointer to an object in memory. When the code in reference
sets Param.Number:
param.Number = 42;
It is getting the reference (pointer) location from param and runnign out
into memory to access the .Number member. It is accessing the same object
in memory as wi, and setting its Number property = 42, so that set "counts"
inside of Main because you passed a reference type.

So, right now it looks like #1 was right.

Now, why is #2 right?

Well, when you call Pass.Reference(wi), you are passing the reference (the
pointer in wi) to the ReferenceMethod by value. If reference tries to
modify the actual value of the pointer (by assigning it to another object),
it will not affect the variable wi in the calling method.

Here's what I mean:
WrappedInt wi = new WrappedInt();
wi.Number = 5;
Pass.Reference(wi);
Console.WriteLine(wi.Number);
// prints "5"

public class Pass
{
public static void Reference(WrappedInt param)
{
// instead of changing .Number, lets try reassign to a new object
// note the difference: I'm reassining param rather than one
// of its members
param = new WrappedInt()
param.Number = 10;
}
}

On the other hand, if you modify Referenct to accept param as a ref
parameter, and then you pass wi as a ref:
Pass.Reference(ref wi)
Console.WriteLine(wi.Number) will print a 10;

Since you passed wi by reference, the Reference method changed the location
in memory that it was pointing to such that it is pointing to the new object
instance. You now don't have any way to access the WrappedInt object that
you originally created in main, and it will be garbage collected and removed
from memory.


I know that was a really long, somewhat contorted explanation. I hope it
helped.
 
J

John Smith

OK, had to read that a *few* times.
But I've got it now.

It really took all of those examples for me to put it together- so thanks
for taking the time!
 
J

Jon Skeet [C# MVP]

z_learning_tester said:
I'm reading the MS press C# book and there seems to be a contradiction.
Please tell me which one is correct, 1 or 2.

I would need to see *exactly* what they wrote to say for sure - it's
easy to rewrite it in your own words and end up with mistakes.
2. Then it goes on to say that whether it is passed by value or reference,
the original arguement is unchanged unless you use 'ref' or 'out'.

That's true.
So what gives? Which is correct, 1 or 2? If 2 is correct, then the example
they gave in 1 shouldn't be valid right? I may be missing something you
would find obvious.

What you're missing is that you can change a value within the object
which a variable refers to without changing the value of the variable
itself.

I've written a page trying to explain all this carefully:
http://www.pobox.com/~skeet/csharp/parameters.html
 
J

J.Marsch

Well, I'm glad it helped. I know that it wasn't the most elegant
explanation available.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top