Passing Strings by Ref
Humbled yet again…DOH! No matter how much experience you acquire, no matter how smart you may be, no matter how hard you study, it is impossible to keep fully up to date on all the nuances of the technology we are exposed to. There will always be gaps in our knowledge: Little 'dead zones' of uncertainty. For me, this time, it was about passing string parameters to functions. I thought I knew this stuff cold. First, a little review...
Value Types and Ref
Integers and structs are value types (as opposed to reference types). When declared locally, their memory storage is on the stack; not on the heap. When passed to a function, the function gets a copy of the data and works on the copy. If a function needs to change a value type, you need to use the ref keyword. Here's an example:
// ---- declaration -----------------
public struct MyStruct
{
public string StrTag;
}
// ---- functions -----------------------
void SetMyStruct(MyStruct myStruct) // pass by value
{
myStruct.StrTag = "BBB";
}
void SetMyStruct(ref MyStruct myStruct) // pass by ref
{
myStruct.StrTag = "CCC";
}
// ---- Usage -----------------------
protected void Button1_Click(object sender, EventArgs e)
{
MyStruct Data;
Data.StrTag = "AAA";
SetMyStruct(Data);
// Data.StrTag is still "AAA"
SetMyStruct(ref Data);
// Data.StrTag is now "CCC"
}
No surprises here. All value types like ints, floats, datetimes, enums, structs, etc. work the same way.
And now on to...
Class Types and Ref
// ---- Declaration -----------------------------
public class MyClass
{
public string StrTag;
}
// ---- Functions ----------------------------
void SetMyClass(MyClass myClass) // pass by 'value'
{
myClass.StrTag = "BBB";
}
void SetMyClass(ref MyClass myClass) // pass by ref
{
myClass.StrTag = "CCC";
}
// ---- Usage ---------------------------------------
protected void Button2_Click(object sender, EventArgs e)
{
MyClass Data = new MyClass();
Data.StrTag = "AAA";
SetMyClass(Data);
// Data.StrTag is now "BBB"
SetMyClass(ref Data);
// Data.StrTag is now "CCC"
}
No surprises here either. Since Classes are reference types, you do not need the ref keyword to modify an object. What may seem a little strange is that with or without the ref keyword, the results are the same: The compiler knows what to do.
So, why would you need to use the ref keyword when passing an object to a function?
Because then you can change the reference itself…ie you can make it refer to a completely different object. Inside the function you can do: myClass = new MyClass() and the old object will be garbage collected and the new object will be returned to the caller.
That ends the review. Now let's look at passing strings as parameters.
The String Type and Ref
Strings are reference types. So when you pass a String to a function, you do not need the ref keyword to change the string. Right? Wrong. Wrong, wrong, wrong.
When I saw this, I was so surprised that I fell out of my chair. Getting up, I bumped my head on my desk (which really hurt). My bumping the desk caused a large speaker to fall off of a bookshelf and land squarely on my big toe. I was screaming in pain and hopping on one foot when I lost my balance and fell. I struck my head on the side of the desk (once again) and knocked myself out cold. When I woke up, I was in the hospital where due to a database error (thanks Oracle) the doctors had put casts on both my hands. I'm typing this ever so slowly with just my ton..tong ..tongu…tongue.
But I digress. Okay, the only true part of that story is that I was a bit surprised.
Here is what happens passing a String to a function.
// ---- Functions ----------------------------
void SetMyString(String myString) // pass by 'value'
{
myString = "BBB";
}
void SetMyString(ref String myString) // pass by ref
{
myString = "CCC";
}
// ---- Usage ---------------------------------
protected void Button3_Click(object sender, EventArgs e)
{
String MyString = "AAA";
SetMyString(MyString);
// MyString is still "AAA" What!!!!
SetMyString(ref MyString);
// MyString is now "CCC"
}
What the heck. We should not have to use the ref keyword when passing a String because Strings are reference types. Why didn't the string change? What is going on?
I spent hours unssuccessfully researching this anomaly until finally, I had a Eureka moment:
This code:
String MyString = "AAA";
Is semantically equivalent to this code (note this code doesn't actually compile):
String MyString = new String();
MyString = "AAA";
Key Point: In the function, the copy of the reference is pointed to a new object and THAT object is modified. The original reference and what it points to is unchanged.
You can simulate this behavior by modifying the class example code to look like this:
void SetMyClass(MyClass myClass) // call by 'value'
{
//myClass.StrTag = "BBB";
myClass = new MyClass();
myClass.StrTag = "BBB";
}
Now when you call the SetMyClass function without using ref, the parameter is unchanged...just like the string example.
I hope someone finds this useful.
Steve Wellens
CodeProject