ref on Reference Type?!

by KodefuGuru 12. June 2009 17:02

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.

Tags: , , , ,

Kodefu

Comments

6/12/2009 5:04:38 PM #

trackback

Trackback from DotNetKicks.com

ref on Reference Type?!

DotNetKicks.com

6/12/2009 5:08:15 PM #

trackback

Trackback from DotNetShoutout

ref on Reference Type?!

DotNetShoutout

6/12/2009 11:15:04 PM #

Justin James

You forgot one extraordinarily important scenario that pass-by-reference addresses, and that's *memory*. Picture this: you have a variable holding, say, 100 MB of data (maybe a large DataSet object, or possibly a System.Drawing.Image). And you start passing it around by value. Do you *really* want that 100 MB duplicated in memory each time you pass it? Nope! So, if you *really* trust your code ([Pure] is useful here Smile ) to not mangle the value, passing by reference will save a ton of memory.

J.Ja

Justin James United States

6/12/2009 11:55:16 PM #

Chris Eargle

Both of those examples are reference types, so it's not going to make a copy (well, okay it will copy the 4 bytes that are on the stack that reference the heap). And if you have a value type that takes up 100 MB, you've got a problem since you typically only have 1 MB of stack memory (see www.kodefuguru.com/.../...-Structs-Everywhere.aspx and www.kodefuguru.com/.../More-On-Structs.aspx)

Chris Eargle United States

6/13/2009 7:53:04 AM #

joe

Justin-
A large dataset object or System.Drawing.Image is a reference type (object). Passing the variable by value (normally) does not make a copy of the data as you imagine it does. The author of the code that insipred the original post also has the same misunderstanding. Passing by reference will save zero memory. Please educate yourself on the issue and develop and understanding. [I am not trying to be harsh; sorry if it comes out that way]

A variable containing a reference type is a reference - currently a 4-byte (I think) data structure (think of it as a pointer, if you want to). Passing this variable by value around is just like passing around a single integer, not a giant image blob or dataset.

As Chris already pointed out.

There are very few scenarios where you want to allow your reference to be changed - i.e., pass a reference type by reference. It is typically dangerous, and it is an incredibly bad code smell; "out" is reasonable scenario - when we are talking about filling value types and need "success" indications - the various TryParse() methods in the framework, for example.

joe United States

6/13/2009 9:39:59 AM #

Justin James

Gah. This is what I get for jumping the gun on a comment. I let my early years of BASIC and Pascal take over my brain for a minute, and override what I know about OOP. Smile Sorry about that, and for making myself look like a total putz!

J.Ja

Justin James United States

6/15/2009 3:20:52 AM #

pingback

Pingback from blog.cwa.me.uk

Reflective Perspective - Chris Alcock  » The Morning Brew #368

blog.cwa.me.uk

6/15/2009 2:09:09 PM #

pingback

Pingback from adam.hathcock.us

Never do this: ref keyword. «  Adam: Be Explicit

adam.hathcock.us

6/19/2009 2:49:20 AM #

Leyu

www.yoda.arachsys.com/csharp/parameters.html

Another detailed explanation covering passing value and reference types using the default, ref or out keywords

Leyu

6/19/2009 10:28:44 AM #

chris

That site has a lot good info, thanks Leyu.

chris United States

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading



Powered by BlogEngine.NET 1.6.0.0
Theme by Mads Kristensen

Whois KodefuGuru

Chris Eargle

Chris Eargle
.NET Community Champion

LinkedIn Twitter Technorati Facebook

MVP - Visual C#

 

INETA Community Champions
Friend of RedGate
Telerik .NET Ninja
Community blogs & blog posts

I am a #52er


World Map

RecentComments

Comment RSS

Tag cloud

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010