What Is the Use of "Ref" for Reference-Type Variables in C#

What is the use of ref for reference-type variables in C#?

You can change what foo points to using y:

Foo foo = new Foo("1");

void Bar(ref Foo y)
{
y = new Foo("2");
}

Bar(ref foo);
// foo.Name == "2"

ref keyword and reference types

Let

class A
{
public string Blah { get; set; }
}

void Do (ref A a)
{
a = new A { Blah = "Bar" };
}

then

A a = new A { Blah = "Foo" };
Console.WriteLine(a.Blah); // Foo
Do (ref a);
Console.WriteLine(a.Blah); // Bar

But if just

void Do (A a)
{
a = new A { Blah = "Bar" };
}

then

A a = new A { Blah = "Foo" };
Console.WriteLine(a.Blah); // Foo
Do (a);
Console.WriteLine(a.Blah); // Foo

What is the advantage of using the ref keyword with a reference type parameter?


What do I gain, as the developer, by passing a reference type argument to a method using the ref keyword if you are not changing what the reference points to?

None whatsoever - that's the only purpose of passing a reference type by reference is if you are going to change what the reference refers to.

To be fair - it gives you the ability to change that behavior in the future, which would be sadistic and cruel...

c# Reference-Type by ref - Address to an Address?

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. [...]

Reference type still needs pass by ref?

Everything is passed by value in C#. However, when you pass a reference type, the reference itself is being passed by value, i.e., a copy of the original reference is passed. So, you can change the state of object that the reference copy points to, but if you assign a new value to the reference you are only changing what the copy points to, not the original reference.

When you use the 'ref' keyword it tells the compiler to pass the original reference, not a copy, so you can modify what the reference points to inside of the function. However, the need for this is usually rare and is most often used when you need to return multiple values from a method.

An example:

class Foo
{
int ID { get; set; }

public Foo( int id )
{
ID = id;
}
}

void Main( )
{
Foo f = new Foo( 1 );
Console.WriteLine( f.ID ); // prints "1"
ChangeId( f );
Console.WriteLine( f.ID ); // prints "5"
ChangeRef( f );
Console.WriteLine( f.ID ); // still prints "5", only changed what the copy was pointing to
}

static void ChangeId( Foo f )
{
f.ID = 5;
}

static void ChangeRef( Foo f )
{
f = new Foo( 10 );
}

Passing a reference type as a parameter by ref

When you pass by reference, you are effectively creating an alias for a variable, so in Switcharoo, pValue is an alias for x in the Go method. As a result assigning to pValue is an assignment to x.

The type of x in Go is Thing, and at runtime this is initially pointing to an instance of the class Animal. After calling Switcharoo, x is pointing to an instance of the Vegetable class instead. The original Animal instance is now unreachable and can be collected.

When using ref is it the variable which is passed by reference, so it works the same way for references and value types like int. In Go, x will (probably) exist on the stack and before calling Switcharoo its value will be the address of the Animal instance. Inside Switcharoo, pValue is an alias for the variable x. This may be implemented as a pointer to the variable in Go but the semantics of ref do not require using pointers.

The specification describes the semantics of ref parameters:

5.1.5 Reference parameters

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.

Does it make sense to pass a reference type to a method as a parameter with 'ref' key?


It lets you change the reference variable itself, in addition to the object it's pointing to.

It makes sense if you think you might make the variable point to a different object (or to null) inside your method.

Otherwise, no.

Underlying implementation of ref for value types

value types are copied, reference types are passed by reference. If you ref a struct you get a pointer to the struct. if you ref a reference type you get a pointer to the instance.

ref value types are useful for being able to change the value the calling function has.
ref reference types are useful for being able to change which instance the calling function has.

Hood peeks:

Depending your performance needs you may not need to know any of this:
https://blogs.msdn.microsoft.com/ericlippert/2009/04/27/the-stack-is-an-implementation-detail-part-one/

Reference types are created on the heap, Value types are usually created on the stack.



Related Topics



Leave a reply



Submit