What's the big deal between passing a value around by val or by ref?
Thursday, May 14, 2009 10:14 PM
This week at our study group we had a discussion about passing values around by val and by ref. How so? Please read on for further details.
Passing values to a method, sounds easy enough eh? hhhhmmmmm since when is anything THAT simple? Ok, coffee in the morning tastes good, ya, that's simple. haha But passing a variable into a method can get confusing pretty quickly.
So what's the big deal you ask eh? Good question! Well, first off, you have primitives (ex int) vs reference types (string, or some class you create out of thin air). Primitives are passed around by value on the stack. Reference types have a pointer/address on the stack pointing to the the actual value on the heap. Sounds easy enough, but what gets complicated is when you start passing values around to other methods, changing those values and maybe seeing the value change on the way back.
When you pass a primitive to a method, a copy of the value on the stack is pushed onto the stack for the calling method to use. Did you get that? Read it over again, A COPY IS PUSHED ONTO THE STACK for the other method to use. Why am I over emphasising this you ask? Cause when the method is done and it goes out of scope and all it's values get pushed off the stack, including the copied variable value, who's value could have been changed in the method, it's all lost! So when you return BACK to the calling method, it still has the original value, untouched by anyone else, still as prestine as the millisecond it was copied over.
The source code has an example of calling a method by val and by ref with an int, string and custom object. Here's a quick picture of what it looks like at runtime.
A picture's worth a thousand word's eh? Thanks for SG for the dual column idea for the illustrations.
For the int, by val example, notice how the value of i on the stack is copied over to age, still on the stack. Next, ChangeByVal will change the age value, then when it's done computing, all of ChangeByVal's "things" on the stack are popped off, including the value of age. Notice how age is it's own separte identity and has nothing to do with i anymore? That's the key to understanding the "by val" concept.
Next, we'll cover the by ref example. The magic here is the CLR recognizes when values are being passed with the ref keyword in the method declaration that it needs to do a bit of additional work as the method finishes, namely copying any address spaces back to the calling method on the stack.
Now, yes, I understand this brief CLR explanation is a gross over simplification and Anders Hejlsberg might be cursing my name, BUT it does help give you a quick and brief explanation of what the difference is between passing values by val and by ref. By the way, if you still haven't figured it out, by val is the default behaviour for .NET. You actually have to explicitly demand the by ref changes, and yes, this is more desirable. This is not to be confused with passing out a reference to a FavouriteNhlTeam and within that object, it points to one team this year, and as they TANK, you switch teams next year, that's a reference within a reference. And that's ok too.
Now that you know a little bit more about the CLR internal workings, it's time to grab a coffee and get coding!
PS The astute audience is probably SCREAMING at their LCDs about the out keyword. The out and ref keywords (from what I can tell and read from Jeffrey Richter, CLR via C#) is they are used interchangeable, EXCEPT for the small change that ref variables be initialized BEFORE being passed out. Whereas out can be passed around before being initalized, but they'll eventually have to be initialized at some point. Uh why? If you're not going to initialize it or use it, then you have to ask why are you passing it around? LOL
Source Code: http://www.pchenry.com:8080/svn/blog/trunk/2009/ByValByRef