when to pass byRef and when to pass byVal - A world apart from the everday ...

A world apart from the everday ...

Assert.IsTrue(Entries.Count == 0);

when to pass byRef and when to pass byVal

I realise that you all think this topic has been covered at nauseam in the past but I was horrified recently in an interview when I asked a question at the response I got from a number of so called "senior C# developers". So then I thought I'd check with colleagues, friends, shopkeepers, gardners, pets, in fact everybody hoping and praying to get a decent answer ... surprisingly I got very few correct answers.

So let me now ask the .net development community of SA the following question, and before looking further for the answer and my explanation try and answer this for yourselves. I'd be interested to know your thoughts on this (common i said no cheating!)

What is the output from the following code extract:-


1    using System;
2    namespace ConsoleApplication2
3    {
4        class ByRefByVal
5        {
6            static void Main(string[] args)
7            {
8                Foo f = new Foo();
9                f.SomeString = "initial";
10                Console.WriteLine(f.SomeString);
11           
12                DoSomething(f);
13                Console.WriteLine(f.SomeString);
14
15                Console.ReadLine();
16            }
17   
18            private static void DoSomething(Foo myfoo)
19            {
20                myfoo.SomeString = "something new";
21                myfoo = null;
22            }
23        }
24
25        class Foo
26        {
27            public Foo() {}
28            public string SomeString=string.Empty;
29        }
30    }

   

Answer: remember no cheating, you'll thank me for this one day :)
initial
something new

Why Larry, Why? I hear you shout, I passed the foo object into DoSomething byVal why was it changed?

Well did you really pass the foo object into the method call? What really happened here?

We all know the difference between passing value types byval and byref,
If the variable is passed byval any change to the variable value in the called function is not reflected back in the callee because a copy of the variable is passed which is killed upon returning from the called function.
If the value is passed byref it means that the changes will be reflected back because the same variable is passed.

So why did this code produce the result it did?
To answer this we need to take a look at what happens when a reference type is passed by byval.

For ordinary method calls passing a variable as an argument to a method (or property) is logically equivalent to declaring another variable of the same type, and assigning its value to the newly-declared variable.
No surprise, there. For both value- and reference-types, a shallow copy of the variable is made.

For value-types, this means a member-wise copy is created.
For reference-types, only the reference is copied

A Brief Insight into Reference Types:
Data for Reference types is stored on the heap. When creating an instance of a reference type a pointer is created on the stack referencing the memory allocated on the heap to the actual object.
Hence Foo foo = new Foo(); actually returns back the pointer.

Now when this pointer is passed by val a copy of the POINTER is made! This new pointer still points to the same memory on the heap and therfore indirectly to the same object; any changes done to the object in the called method will manipulate the same data to which original foo pointer was pointing.
But notice that when I set foo=null on line 21 nothing happened outside of DoSomething, that's because I never killed the object on the heap, just the myFoo pointer [the byval param of the method] and the original pointer created in the Main method still lives and points happily to the object on the heap.

In case of passing byref, a reference to the original pointer is passed to the called function.
So is there a difference between passing byref and passing byval when dealing with reference types? Yes, but a rather subtle one.

Lets take a look at the slightly modified version of above code:
What do you suppose the output of this code will be?

1    using System;
2    namespace ConsoleApplication2
3    {
4        class ByRefByVal
5        {
6            static void Main(string[] args)
7            {
8                Foo f = new Foo();
9                f.SomeString = "initial";
10                Console.WriteLine(f.SomeString);
11           
12                DoSomething(ref f);
13                Console.WriteLine(f.SomeString);
14
15                Console.ReadLine();
16            }
17   
18            private static void DoSomething(ref Foo myfoo)
19            {
20                myfoo.SomeString = "something new";
21                myfoo = null;
21            }
22        }
23
24        class Foo
25        {
26            public Foo() {}
27            public string SomeString=string.Empty;
28        }
29    }


Now you can see I'm passing around a reference to a pointer.
If you run the above code it breaks because the calling method attempts to access a null object on line 13.
Why you ask, well by setting myfoo = null on line 21 this time I am actually modiying the original pointer created on line 8,
thereby actually whacking the object from the heap.

Based on this you can see why passing reference types byRef is not a great idea, you have no idea whether or not the called function has disposed of your object for you.

As part of my job I get to do regular code review sessions and something I find ALL the time is DataSet objects being passed all over the place byref, when asking the developer why the response is always "to stop new copies of the huge dataset being created each time".

Sorry to burst your little "performance" bubble fella, but passing a dataset byval WILL NOT create a new instance of the object.
You will merely create a new pointer to the same object which you are free to do with as you wish.

Of course this begs the question: Can anybody think of a time when you have to pass a reference type by ref?
Posted: Nov 08 2005, 04:27 PM by Ryan CrawCour | with 8 comment(s)
Filed under:

Comments

Ernst Kuschke said:

Change the Foo class to a struct, and pass that to DoSomthing by reference, and you'll see the behavior you'd expect ;o)

using System;

namespace DoSomthing
{
class ByRefByVal
{
static void Main(string[] args)
{
Foo f = new Foo();
f.SomeString = "initial";
Console.WriteLine(f.SomeString);

DoSomething(ref f);
Console.WriteLine(f.SomeString);

Console.ReadLine();
}

private static void DoSomething(ref Foo myfoo)
{
myfoo.SomeString = "something new";
}
}

struct Foo
{
public string SomeString;
}
}

Results:
initial
something new
# November 8, 2005 9:25 PM

Ryan CrawCour said:

That kinda defeats the object ... changing it to a struct makes Foo a value-type. I was attempting to illustrate what happens when you pass a reference-type by value.

Don't think we could ask MS to change their dataset class to a struct now can we just to get the results we expect.

It is important to know how things work and how to get the expected result the proper way, well at least it is important to some of us :)
# November 9, 2005 8:15 AM

Simon Stewart said:

This makes for pretty interesting reading.
I always thought that an object was automatically passed byref and not via the reference copying that you've shown.

I don't know the internal workings of .NET objects well enough to explain why. When Mario L gets round to reading this, I'm sure he'll give a good explanation.
# November 9, 2005 11:17 AM

Ernst Kuschke said:

I was illustrating that passing ValueTypes by ref has the effect that some of your guys might expect from RefTypes.

You can't change the behavior of RefTypes. Passing by ref is intended mostly for Value Types in the first place.

Quiz: What's the difference between passing a referencetype by value, and passing a valuetype by reference? :P
# November 9, 2005 3:29 PM

Ernst Kuschke said:

Ahhh, now there is the big misunderstanding.
In .NET, reference types are *NOT* passed by reference by default, as is popular belief. A *reference* to the reference type is passed by *value* by default ;o)
# November 9, 2005 3:32 PM

Senkwe said:

You asked "Can anybody think of a time when you have to pass a reference type by ref"

I can't think of a good reason off the top of my head. But my first guess is that the presence of the "ref" keyword should act as a clue to callers that you're not dealing with a copy of an object reference, but the reference itself. Now, why someone would do that in the first place isn't clear to me right now
# November 9, 2005 7:06 PM

malio said:



Hi Simon

The post is essentially correct,

barring a few technical inaccuracies ... i.e.
['Now you can see I'm passing around a reference to a pointer'] & ['modiying the original pointer created on line 8,
thereby actually whacking the object from the heap.'] &
[a few others])

and some ambigueties ... i.e.
['not the called function has disposed of your object for you.'] &
[a few others])



Objects in C# and VB.Net are passed byval by default. This implies a copy of a reference is made (only the reference! (aka pointer)) or a copy of the value types data (note: only the data is copied, a value type has no instance v-table for dispatch since value types are sealed) before passing.

(go a level deeper and pointers are involved, but it's a topic in itself)

for further clarification I recommend you have a look at:
http://dotnetjunkies.com/WebLog/chris.taylor/archive/2004/06/13/16364.aspx

for some in depth technical info on value types, this is the red pill:
http://blogs.msdn.com/cbrumme/archive/2003/05/10/51425.aspx


And to answer the authours question, ref arguments are needed when wishing to return different instance of reference types (especially useful if the type is immuatble). ie when modifying a string or the invocation list of a delegate. Also required for numerous interop calls.

MaLio



# November 9, 2005 11:46 PM

Ryan CrawCour said:

Thanks Mario for the comments.
The links you posted were really helpful thanks a mil!

sorry about the "technical inaccuracies" :$
# November 10, 2005 10:14 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: