BMBM
BMBM

Reputation: 16013

c# - ref modifier for ...reference types

I am a bit confused about this piece of code.

    public static void Foo(A p)
    {
        p.SomeProp = "ccc";
        p = null; // !!!
    }

    static void Main(string[] args)
    {
        A p = new A();
        Foo(p);

        Console.WriteLine("SomeProp is: " + p.SomeProp);
        Console.ReadLine();
    }

The output is:

"SomeProp is: ccc"

But I would have expected a NullReferenceException.


However, if I change it like so, using the ref modifier:

    public static void Foo(ref A p)
    {
        p.SomeProp = "ccc";
        p = null;
    }

    static void Main(string[] args)
    {
        A p = new A();
        Foo(ref p);

        Console.WriteLine("SomeProp is: " + p.SomeProp);
        Console.ReadLine();
    }

I get a NullReferenceException - the second one is understandable for me.

But how can it be, that in the first piece of code p is NOT set to null, but the property gets its value set?

My question is: what is the p argument of the Foo method in the first piece of code if it is not a reference to the orginal instance?


Btw. here is the definition of class A

public class A
{
    public string SomeProp;
}

Upvotes: 4

Views: 1208

Answers (9)

Cranialsurge
Cranialsurge

Reputation: 6184

How is it though that if you add "p" to the watch list while debugging and generate an object id for it, that id is the same as the id of "p" inside the function Foo(p). So essentially both "p" in the calling method and "p" in Foo have the same object id.

Upvotes: 0

Nirlep
Nirlep

Reputation: 566

When you pass an object reference by valule you can change its states. But when you pass an object reference by reference you can not only change its states but actual object itself.

Edited: After knowing objects are never passed as value or reference but a reference to an object is passed as value or reference.

Upvotes: -1

ICR
ICR

Reputation: 14162

p.SomeValue = "ccc";

is saying:

  • Get the object that p is a reference to
  • Set the value of the property SomeValue on that object to "ccc"

    p = null;

is saying:

  • Change p to, instead of referring to the object it used to, now refer to null.

It is not saying change the object that p refers to to null, but that the local variable p should now refer to null.

By default, when you pass an argument of type A such as in the method invocation "Foo(p)" you are not passing the object that is referenced by p, or even the reference p, but a reference to the object referenced by p. They reference the same object, but they are not the same reference. i.e. the reference p in "public static void Foo(A p)" is not the same reference as the p in "Foo(p)", but they do reference the same object.

You can alter this behaviour by using a ref parameter instead. This changes it so that they are the same reference, and altering the value of one alters the value of the other.

Upvotes: 6

Jay Riggs
Jay Riggs

Reputation: 53593

Here's an article with a graphical representation of what you're seeing:

Parameter passing in C# (Lee Richardson)

I found this article really helpful.

And I'm surprised nobody's linked Jon Skeet's article (from which I found the link above):

Parameter passing in C# (Jon Skeet's blog)

Upvotes: 2

Brian Rasmussen
Brian Rasmussen

Reputation: 116401

C# uses pass by value unless you use out/ref. When you pass the reference by value. The reference is copied. However, since it still refers to the same object on the heap, you can modify the state of the object via the reference. If you use ref your passing the address of the reference. Nulling this reference nulls the actual reference and hence the NullReferenceException when you access the original reference after that.

Upvotes: 1

Robert Cartaino
Robert Cartaino

Reputation: 28112

Object references are passed by value. <-- Very important.

Your method has a copy of the reference so, when you say p = null, you are only changing the copy of that reference. When you return, the original reference 'p' still has it's original value.

In the second example, you pass your object by reference explicitly. So, the new reference (null) is passed back to your calling function (which then gives you the null exception).

Upvotes: 4

Adam Wright
Adam Wright

Reputation: 49376

The p argument is a copy of the reference to the new Foo instance you made. Think about it as a signpost: calling "new A()" creates a A object on the heap, and gives you back a signpost to it, that you store in p. You then call the Foo function, and give it a copy of the signpost - it knows how to get to the A object, and updates the property. It then scribble all over the signpost - it can't get there any more, but the object still exists. The caller still has a valid signpost, so no exception is thrown.

The second instance, with the "ref" parameter effectively says "Don't give me a copy of the signpost, give me the actual signpost". This time, when it scribbles all over it, the original that was passed to the function is lost as well, and the exception occurs.

In more technical terms, C# is, without the ref keyword, always "Call by value" - all parameters are passed copies of the value of the argument. The fact that the value of the argument is a signpost (or "reference"), doesn't matter.

Upvotes: 3

Joel Coehoorn
Joel Coehoorn

Reputation: 415735

In .Net, everything is passed by value unless you explicitly use the ref or out keywords. For reference types, that means passing a copy of the reference.

In your first example, that means your p variable is still a reference to the same object, and so setting a property works like you would expect. But when you set the reference itself to null, all you changed was the copy.

Upvotes: 17

Eric
Eric

Reputation: 6425

In the first function, you are only setting the local reference to p to null. You are not setting the p in main to null. In the second function, by virtue of the use of ref, you are setting the p in main to null.

Upvotes: 1

Related Questions