How to Return a Reference to a Variable in C#

Is it Possible to Return a Reference to a Variable in C#?

Update

This feature has been added to C# 7. You can use syntax just like you posted in your question. For example:

double[,,] doubleArray = new double[10,10,10];

ref double GetElement()
{
var (x,y,z) = (1,2,3);
return ref doubleArray[x, y, z];
}

Eric Lippert's answer goes into detail. I would probably delete this answer, but as it's the accepted answer I cannot delete it.

Original Answer

Value types in C# are always passed by value. Objects always have their reference passed by value. This changes in "unsafe" code as Axarydax points out.

The easiest, safest way to avoid this constraint is to make sure that your double is attached to an object somehow.

public class MyObjectWithADouble {
public double Element {get; set;} // property is optional, but preferred.
}

...
var obj = new MyObjectWithADouble();
obj.Element = 5.0

I also want to remark that I'm a little confused about how you anticipate assigning a double to a three-dimensional array. You might want to clarify what you're going for.

I think I understand a little better what you're going for now. You want to return the location of the value in a given array, and then be able to change the value in that location. This pattern breaks some of the expected paradigms of C#, so I would suggest considering other ways to achieve what you're looking for. But if it really makes sense to do it, I'd do something more like this:

public class 3dArrayLocation {public int X; public int Y; public int Z;}

...

public 3dArrayLocation GetElementLocation(...)
{
// calculate x, y, and z
return new 3dArrayLocation {X = x, Y = y, Z = z}
}

...

var location = GetElementLocation(...);
doubleArray[location.X, location.Y, location.Z] = 5.0;

Why can I return a reference to a local variable's field

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.

Return By Reference in c#?

With the risk of sounding mean, you should read the C# reference :)

C# divides things into reference types and value types. Reference types are as you can imagine, being passed by reference. This means a reference to the object is passed.

Here is a contrived example of return by reference:

class MyClass // <- Reference type.
{
private MyClass _child = new MyClass();

public MyClass GetChild()
{
return _child;
}
}

Value types are passed by value; although I imagine under the hood something else could be going on. This is not important to you though, only the behaviour is important.

Example of value types: int, char, Color...

You create a reference type through class, and a value type through struct.

What kinds of objects can return by reference in C#

The key take-away for the rules for managed references (ref) is: a managed reference must not point to a local variable, or to part of one (in the case of a struct), because the reference can outlive the life of the location it points to. It must point to a non-stack location.

Let's take each version one-by-one



        ref int RefReturn()
{
int[] a = { 1, 2 };
return ref a[0];
}

In the above example, the returned reference points to the interior of the array, it does not point to the local variable. The interior of an array is effectively a field of a heap object. The array will outlive the life of the function.



        ref int RefReturn2()
{
Test t = new Test();
return ref t.x;
}

In this one, Test is a reference-type, and therefore lives on the heap. The reference points to the field x of the object contained in t, this also lives on the heap. The fact that t is a local variable is immaterial, the reference does not point to t.



        ref int RefReturnError()
{
int a = 1;
return ref a; //Error
}

In this case, the reference points to the actual location of the local variable, this lives on the stack, and the location will disappear at the end of the function.

Note that the same problem is visible when taking a reference to a field of a struct, when the struct's location is a local variable.

        ref int RefReturnError1A()
{
MyStruct a = new MyStruct();
return ref a.x; //Error
}


        ref Test RefReturnError2()
{
Test t = new Test();
return ref t; //Error
}

In this one, although t is a reference-type and itself points to a heap object, our reference does not point to that object which t points to. It points to the location of t itself which contains that object reference.


Note that a reference to a boxed struct is disallowed for a different reason: due to C#'s unboxing rules, unboxing (logically) creates a copy, therefore you cannot change it in place. Coding in IL directly (or in C++/CLI) you can perfectly verifiably do the equivalent of:

        ref int RefReturnBox()
{
object a = (object)1;
return ref (int)a; // CS0445: Cannot modify the result of an unboxing conversion
}

How to return a reference to a string in c#?

To do this you need to write a setter property. Unfortunately, setters can’t take further arguments in C# so you won't be able to write this code 1:1 in C#. The closest you can get is a nested class with a default property:

class YourClass {
public class Nested {
public Nested(YourClass outer) { m_RefToOuterWorld = outer; }
private readonly YourClass m_RefToOuterWorld;

public string this[int index] {
get { return m_RefToOuter.TestArray[index];
set { m_RefToOuter.TestArray[index] = value; }
}
}

private readonly Nested m_Nested;
private string[] TestArray = new string[10];

public YourClass() { m_Nested = new Nested(this); }

public Nested TestIt { get { return m_Nested; } }
}

You can use it like this:

var test = new YourClass();
test.TestIt[2] = "Hello world!";

By the way, since this is so much effort, you probably don't want to do this. Also, it doesn't feel very C#-y. The useless indiretion through the nested class here isn't something you'll see very often.

C# method returns int-by value or reference

The value in callerMethod() is a new value, independent of the value in returnInt().

I hope the example below helps.

static int myStaticInt = 333;
public static int returnInt()
{
return myStaticInt;
}

public static void callerMethod()
{
var i = returnInt();
i += 100;
Console.WriteLine(i);
}

public async static Task Main(string[] args)
{
Console.WriteLine(myStaticInt);
callerMethod();
Console.WriteLine(myStaticInt);
}

Output

333
433
333

Result

myStaticInt is still 333.


Here is a relevant part from Passing Value-Type Parameters (C# Programming Guide):

A value-type variable contains its data directly as opposed to a reference-type variable, which contains a reference to its data. Passing a value-type variable to a method by value means passing a copy of the variable to the method. Any changes to the parameter that take place inside the method have no affect on the original data stored in the argument variable.

Here is a bit about assignment from Types part of C# specification.

Assignment to a variable of a value type creates a copy of the value being assigned. This differs from assignment to a variable of a reference type, which copies the reference but not the object identified by the reference.


Passing and returning value types by reference

Please note that it is possible to pass, and return, value types by reference.

Please see Passing Value Types by Reference section of Passing Value-Type Parameters (C# Programming Guide) and Ref returns and ref locals.

Equipped with this knowledge, let's now modify our example and examine the result.

static int myStaticInt = 333;
public static ref int returnInt()
{
//var returnVal = 1;
return ref myStaticInt;
}

public static void callerMethod()
{
ref var i = ref returnInt();
i += 100;
Console.WriteLine(i);
}

public async static Task Main(string[] args)
{
Console.WriteLine(myStaticInt);
callerMethod();
Console.WriteLine(myStaticInt);
}

Output

333
433
433

Result

myStaticInt is now 433, not the original 333.

Where does a value type-variable - which is returned by ref - live? Stack or heap?

I feel like you understand yourself already why it does not work. You cannot return local variable by reference from method (unless it's ref local), because in most cases lifetime of local variable is the method, so its reference outside of method does not have any meaning (outside of method this variable is dead and location where it were before might contain anything). As documentation 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

In practice some local variables might live longer than execution of method they are declared in. For example, variables captured by closure:

int myLocal = 5;
SomeMethodWhichAcceptsDelegate(() => DoStuff(myLocal));
return ref myLocal;

However, this introduces additional complications without any benefits, so this is also forbidden, even though lifetime of myLocal might be much longer than containing method.

It's better to not think about it in terms of stack and heap. For example you might think that you cannot return reference to something allocated on stack from the method via ref return. That's not true, for example:

private void Test() {
int myLocal = 4;
GetX(ref myLocal);
}

private ref int GetX(ref int i) {
return ref i;
}

Here myLocal is clearly on stack, and we pass it by reference to GetX and then return this (stack allocated) variable with return ref.

So just think about it in terms of variable lifetimes and not stack\heap.

In your second example, lifetime of _myInt field is clearly longer than execution of GetX, so there is no problem to return it by reference.

Note also that whether you return value type or reference type with return ref doesn't make any difference in context of this question.



Related Topics



Leave a reply



Submit