Passing Reference Types By Value

A lot of people seemed confused (see this post) when passing reference types by value in the .NET Framework, as described by example 4 in this MSDN article:

C# Language Features : Passing Parameters

I can see why it is confusing but it makes sense ... when you pass a reference type by value, it passes in a copy of the reference, not a copy of the object. You now have two pointers on the stack to the same object on the heap. So while you may access the object through different references, the same object is modified.

So why isn't a copy of the object made when a reference type is passed by value? Because it's not so easy depending on the object. The example of an array of integers is kind of weak because it's easy to copy an array of integers -- in fact, it would be an understandable assumption that an array of integers is a value type, not a reference type as it happens to be -- but more on that in a moment. To answer the quesiton, for complex objects that have various constructors, etc., you can't simply copy the object. The class itself would need to implement that support, and fortunately it's standardized through the ICloneable Interface (see this page) that lets the class implement how to copy itself.

If a situation warrants a lot of value types in a class, then use a struct instead. The struct will keep all members that are also primitives on the stack, so it will illicit the perceived correct behavior when passing by value. You can demo this with some simple code:


    public struct TestStruct
    {
        public int a;
    }

    public class TestClass
    {
        public int a;
    }

    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            TestClass tc = new TestClass();
            TestStruct ts = new TestStruct();

            Console.WriteLine("Testing a class:");
            Console.WriteLine(tc.a);
            TestClassMethod(tc);
            Console.WriteLine(tc.a);

            Console.WriteLine("Testing a struct:");
            Console.WriteLine(ts.a);
            TestStructMethod(ts);
            Console.WriteLine(ts.a);
        }

        public static void TestClassMethod(TestClass test)
        {
            test.a = test.a + 3;
            Console.WriteLine(test.a);
        }

        public static void TestStructMethod(TestStruct test)
        {
            test.a = test.a + 3;
            Console.WriteLine(test.a);
        }            


In regards to the blog post I referenced above (pun intended), it was noted that you can then call new on the object, which repoints the reference of the object to a new instance. Of course, this makes perfect sense if you understand the reference is copied, not the object, and until new is called on that reference, it still points to the same object as the other reference.

If the receiving method needs to have a "working copy" of the object, you should use ICloneable since it encapsulates that logic into the class. It's much cleaner. If you're a new developer and still have some confusion on the topic, be sure to read MSDN's article on Types, found here: Type Fundamentals.

Comments (2) -

James Byrd
James Byrd
9/4/2005 4:18:42 AM #

Of course, this discussion might make you wonder why you would want to pass a reference type by reference. Answer: When you want to be able to change the reference itself in the calling procedure.



For example, factory methods generally allocate a new object instance for you and pass a reference back as the return value. But what if you want to use the return value for something else? How do you get back the reference you need? You could pass an object reference as a method parameter. If you pass the parameter by value, the new reference created in the factory method would be thrown away upon return. That is why you need to pass the object reference by reference. That way, when the factory method allocates the new object and sets the object reference that was passed as a parameter, the reference is preserved upon return to the calling procedure.



Brian
Brian
9/4/2005 12:31:43 PM #

James -- perfect!  Yes, excellent point.  I talked about passing the reference as a value but not passing the reference as a reference.  You're spot on with the that.



I imagine there's a lot of potential bugs out there for those who don't understand the difference, and it's a key understanding when working with various patterns and polymorphism.

Comments are closed

My Apps

Dark Skies Astrophotography Journal Vol 1 Explore The Moon
Mars Explorer Moons of Jupiter Messier Object Explorer
Brew Finder Earthquake Explorer Venus Explorer  

My Worldmap

Month List