Where is the value type variable - which is returned by reference? Stack or heap?

I recently heard about the new C # function in 7.2, so now we can return a value type reference (e.g. int ) or even a read link for a value type. Therefore, as far as I know, the type of value is stored on the stack. And when the method is left, they are removed from the stack. So what happens to int when the GetX method GetX ?

 private ref int GetX() { // myInt is living on the stack now right? int myInt = 5; return ref myInt; } private void CallGetX() { ref int returnedReference = ref GetX(); // where does the target of 'returnedReference' live now? // Is it somehow moved to the heap, because the stack of 'GetX' was removed right? } 

I get an error

Error CS8168: Unable to return local link "myInt" by reference because it is not local (11, 24)

So why is this not working? Does this not work just because the variable cannot be moved to the heap? This is problem? can we return value types by reference if they do not live on the stack? I know that these are two questions in one.

First: Where are the values ​​of the variable type returned by the live link? Stack or heap? (I think a bunch, but why)?

Second: why is the value type created on the stack not returned by reference?

So, this can be compiled:

 private int _myInt; private ref int GetX() { // myInt is living on the stack now right? _myInt = 5; return ref _myInt; } private void CallGetX() { ref int returnedReference = ref GetX(); // where does the target of 'returnedReference' live now? // Is it somehow moved to the heap? becase the stack of 'GetX' was removed right? } 

If I understand your comments correctly, is it because _myInt is now not living inside the GetX method, and therefore fore is not created on the stack correctly?

+5
source share
2 answers

I feel that you yourself understand why this does not work. You cannot return a local variable by reference from a method (if it is not specified locally), since in most cases the lifetime of a local variable is a method, therefore its reference outside the method has no value (outside the method, this variable is dead and located where it could contain anything before). As stated in the documentation:

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 a method that returns it

In practice, some local variables can live longer than executing the method in which they are declared. For example, variables captured by closure:

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

However, this leads to additional complications without any advantages, therefore it is also prohibited, although the life of myLocal can be much longer than the containing method.

It’s better not to think about it in terms of stack and heap. For example, you might think that you cannot return a link to something allocated on the stack from a method via ref return . This is not the case, for example:

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

Here myLocal clearly on the stack, and we pass it via the GetX link, and then return this (stack allocated) variable with return ref .

So just think about it in terms of lifetime variables, not stack \ heap.

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

Note also that if you are returning a value type or reference type using return ref , this has no meaning in the context of this question.

+3
source

So, as far as I know, the type of value is stored on the stack.

and thus is the basis of your confusion; this is a simplification that is extremely inaccurate. Structures can live on the stack, but they can also live:

  • like a field for objects on the heap
  • like fields in other structures that (etc.) fields on an object in a heap
  • in a box (directly or through any of the above)
  • in unmanaged memory

You are correct, though: if you passed the ref return from the method to the local one inside the method, you will violate the integrity of the stack. That is why this scenario is not allowed:

 ref int RefLocal() { int i = 42; return ref i; // Error CS8168 Cannot return local 'i' by reference because it is not a ref local } 

There are some scenarios where the compiler can prove that although it was saved as local, the lifetime was limited by this method; this helps not to reassign the local ref (to be honest, this check is the main reason for this restriction); this allows:

 ref int RefParamViaLoval(ref int arg) { ref int local = ref arg; return ref local; } 

Since ref int arg has a lifetime that is not tied to the method, our ref int local can inherit this lifetime in the assignment.


So, what can we profitably return to?

This may be a reference to the inside of the array:

 ref int RefArray(int[] values) { return ref values[42]; } 

This may be a field (not a property) for the object:

 ref int ObjFieldRef(MyClass obj) { return ref obj.SomeField; } 

This can be a field (not a property) in the structure passed by reference:

 ref int StructFieldRef(ref MyStruct obj) { return ref obj.SomeField; } 

This may be something received from a subsequent call if the call does not include any ref locators that are known to point to locals (which would make it impossible to prove the validity):

 ref int OnwardCallRef() { ref MyStruct obj = ref GetMyStructRef(); return ref obj.SomeField; } 

Here again, note that the lifetime of the local inherits the lifetime of any parameters passed to the current call; if the incoming call included ref -local with a limited lifetime, then the result would inherit this limited lifetime, and you could not return it.

And this call can be, for example, a call to structures stored in unmanaged memory:

 ref int UnmanagedRef(int offset) { return ref Unsafe.AsRef<int>(ptr + offset); } 

So: there are a lot of very correct and useful scripts that do not include links to the current stack frame.

+4
source

Source: https://habr.com/ru/post/1275949/


All Articles