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
orout
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
C# Open a New Form Then Close the Current Form
Word Wrap for a Label in Windows Forms
Dot Character '.' in MVC Web API 2 for Request Such as API/People/Staff.45287
How to Make an Event in the Usercontrol and Have It Handled in the Main Form
How to Add an Attribute to a Property at Runtime
The Entity Type <Type> Is Not Part of the Model for the Current Context
Wcf Named Pipe Minimal Example
How to Convert This Foreach Code to Parallel.Foreach
Omitting All Xsi and Xsd Namespaces When Serializing an Object in .Net
Call Signalr Core Hub Method from Controller
Export Datatable to Excel with Epplus
How to Parse (Big) Xml in C# Code
Padding Is Invalid and Cannot Be Removed