0

trying to understand what exactly happens, if I pass a Reference-Type by its reference. I understand it this way:

So I have a Class Foo, which represents a Reference-Type

class Foo{ //Foo-stuff here }

So with this right here

Foo f = new Foo();

Foo f (created on the Stack) holds the address/reference of the actual Object, which is created in the Heap when calling new Foo();

so f actually holds something like 0x2A53BC

If I call a Method doing this

void Test(Foo g) { g = null; }

I just pass the address of the "address-variable/reference-variable" f. So g actually points to f in the Stack but NOT to the actual Object in the Heap. So I can change propertys of the Foo Object in Heap through f, because I have the Address of f, I can access Foo through f, but setting g=null just kills the Address to the f variable in Stack, not the reference to the object in Heap itself.

but with this

void Test(ref Foo g) { g = null; }

Im actually working with the real address of that Foo-Object in the Heap, so with 0x2A53BC. That means, I can change where it points to or Kill the pointer from Stack to Heap by setting g=null

Is this right? Thank you very much!

2 Answers2

0

I just pass the address of the "address-variable/reference-variable" f. So g actually points to f in the Stack but NOT to the actual Object in the Heap.

Actually you have it backwards. When you don't use ref g will have the value of f (in your example 0x2A53BC) copied in to a new variable named g. g and f are totally independent of each other, but they both hold the "value" 0x2A53BC. When you do g = null; the variable f will still hold 0x2A53BC but g will now hold 0x000000.

NOTE: I am not sure about this next part, I may be telling you factually incorrect information, but I am describing the correct behavior from the outside observer.
When you use ref the variable g will point at the address of the value of f (lets say 0x154AFA) and that f still points at 0x2A53BC. When you do g = null; the value of g does not get updated, it still points at 0x154AFA, however f will now point at 0x000000

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
0

So Foo is a class type, in particular a reference type. And you have:

var f = new Foo();

so the value of f is really a reference to the actual object. And as you say, we may think of the reference as an integer, say 0x2A53BC (just to make it concrete, we do not really have access to the integer or whatever the reference really is; there is no pointer arithmetic with these references).

Now, when you pass by value, as with the overload:

void Test(Foo g) { g = null; }

which we call with Test(f);, the value 0x2A53BC will be copied to a new storage location (a new place on the stack), and that new storage location becomes g. If you had used g to mutate the existing instance, then since both storage locations hold identical information 0x2A53BC, you would have mutated the instance pointed to also by f. But instead you choose to assign to g. That leads to a the new storage location for g holding now the reference 0x000000 (we simply assume that zero is used as the representation for null). It clearly will not change the storage location for f which still points to the original instance located at 0x2A53BC.

In contrast, with the other overload:

void Test(ref Foo g) { g = null; }

which we call with Test(ref f);, we pass by reference. So the same storage location is used for g as we already had for f. The value 0x2A53BC is not copied. When you assign g = null;, that is the original 0x2A53BC being overwritten with 0x000000 (the magic representation of null). When the method returns, yes even before it returns, f has changed its value to point to the new "place" 0x000000.

To sum up: The "value" of a reference type is the reference. When passed by value, a copy of the reference "number" is made and used by the invoked method. When passed by reference, the value is not copied, the same storage location is used by the callee method and the caller method.


Addition: Some quotes from the C# spec:

5.1.4 Value parameters

A parameter declared without a ref or out modifier is a value parameter.

A value parameter comes into existence upon invocation of the function member (method, instance constructor, accessor, or operator) or anonymous function to which the parameter belongs, and is initialized with the value of the argument given in the invocation. A value parameter normally ceases to exist upon return of the function member or anonymous function. [...]

5.1.5 Reference parameters

A parameter declared with a ref modifier is a reference parameter.

A reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the function member or anonymous function invocation. Thus, the value of a reference parameter is always the same as the underlying variable. [...]

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181