I recently came across a piece of code that had almost every parameter for every method marked with the ref keyword. This keyword is primarily used to pass a value type by reference, but this one even had reference types marked with the keyword. This made me wonder, why exactly would you ever want to pass a reference type as a ref parameter? Page Brooks and I discussed this at a user group meeting earlier in the week, but neither of us had an answer.
So, I turned to that ever-trusty resource, the MSDN Library. The trick is that passing by ref allows you to change the reference. Take this code for example:
class Program
{
static void Main()
{
var message = new Message("Created by Main");
Console.WriteLine(message.Text);
Method(message);
Console.WriteLine(message.Text);
}
static void Method(Message message)
{
message = new Message("Created by Method");
}
class Message
{
public string Text { get; set; }
public Message(string text)
{
Text = text;
}
}
}
This will have the following output:
Created by Main
Created by Main
The reason “Created by Method” isn’t printed is because the parameter message was given a new reference. The message variable in Main() has its original reference. Now let’s see what happens when we change Method() to use a ref parameter.
class Program
{
static void Main()
{
var message = new Message("Created by Main");
Console.WriteLine(message.Text);
Method(ref message);
Console.WriteLine(message.Text);
}
static void Method(ref Message message)
{
message = new Message("Created by Method");
}
class Message
{
public string Text { get; set; }
public Message(string text)
{
Text = text;
}
}
}
Now our output looks like this":
Created by Main
Created by Method
The ref keyword has modified Method() so that now it will allow the reassignment of message.
Wait, isn’t that just like the out keyword?
Actually, it’s almost identical to the out keyword. I can even replace ref with out in the above example and it will work exactly the same. However, there is one difference: you need to pass an assigned variable with ref, but you can pass an unassigned variable with out. It’s semantically odd to expect an out parameter to be used as input, although nothing prevents you from doing this. It would be best to stick with ref if you wanted in/out.
Now that I’ve explained the behavior one gets using the ref keyword with a reference type, allow me to make a bold claim. The ref and out keywords are code smells.
There’s almost no reason to ever use the ref or out keywords with a reference type. In fact, in the code where I noticed the ref keywords littered everywhere, it wasn’t reassigning parameters in the methods. The ref declarations were merely a waste of space that caused the Spock eyebrow to raise. If you are reassigning the parameter, I recommend that you take another look to see if it can be done another way. For example, the ref version of the method could easily be refactored to return a message object that is assigned to the message variable:
static void Main()
{
var message = new Message("Created by Main");
Console.WriteLine(message.Text);
message = Method(message);
Console.WriteLine(message.Text);
}
static Message Method(Message message)
{
return new Message("Created by Method");
}
I feel that this code is much clearer than using ref. In the method that’s making the call, Main(), it’s clear that message is being reassigned. In the ref version, you have to look through Method() to see whether or not it is being reassigned.
The keywords are there because there are situations where they are useful; DateTime.TryParse() comes to mind. However, like most cool tech in C#, it can be abused.