The documentation on ref returns and ref locals states:
The return value must have a lifetime that extends beyond the execution of the method. In other words, it cannot be a local variable in the method that returns it. It can be an instance or static field of a class, or it can be an argument passed to the method. Attempting to return a local variable generates compiler error CS8168, "Cannot return local 'obj' by reference because it is not a ref local."
I thought I understood this. I figured if I returned a reference to a local variable, then that variable might be collected and so my reference no longer references a variable.
However, I learned today that I can create a local variable then return a reference to a field on that local variable. To illustrate:
using System;
namespace IAmConfused
{
class Program
{
static void Main(string[] args)
{
var foo = new Foo();
ref int barInt = ref foo.GetInt();
Console.WriteLine(barInt); //Outputs 123
barInt = 354;
Console.WriteLine(barInt); //Outputs 354
}
}
public class Foo
{
public ref int GetInt()
{
// int x = 123;
// return ref x; //CS8168
var bar = new Bar(123);
return ref bar.Value;
}
class Bar
{
public Bar(int v)
{
Value = v;
}
public int Value;
}
}
}
How is this different from just returning a local? The local variable bar
might be collected after GetInt
returns, right? So what is barInt
referencing if that happens?
I've tried C#
versions 7.0, 7.1, 7.2, and 7.3 and it works on all of them.
Consider this code example:
void Foo()
{
object a;
int b;
}
Both of these variables are held on the stack. Yes, they really are, even the object. The variable a
is a pointer to the object, not the object itself, and that pointer is indeed a local variable on the stack.
So if you return a reference to either of these, it'll be a reference to some variable that was on the stack. The stack of course is overwritten when the call returns, which is why you can't ref return a local.
On the other hand, consider this example:
class Bar
{
public int SomeInteger;
}
void Foo()
{
Bar c = new Bar();
}
In this case, c
is held on the stack as well. But the object that it references is on the heap, as is its field, SomeInteger
. So you can safely return a reference to SomeInteger
since it exists on the heap and the heap is not overwritten when the method returns.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With