Search code examples
c#pointersrefunsafebyref

Assign a value by ref to a class member by ref


Something is confusing me, and I hope you can maybe clarify.

// This class assumes that T is definitively compatible to TheStruct
class ValueHolder<T> {
  T _value;
  TheStruct _structValue;

  void Test1() {
    _structValue = ref Unsafe<T, TheStruct>(ref _value); // ERROR: _structValue is not a by-ref value
    // _structValue = Unsafe<T, TheStruct>(ref _value); // WORKS: Copy from stack to heap
  }

  void Test2() {
    ref var structValue = ref _structValue;
    structValue = ref Unsafe.As<T, TheStruct>(ref _value); // WORKS: Why, and what implication does it have?
  }
}

So my question is in Test2: Why I can assign Unsafe.As as ref, when defining ref var structValue = ref _structValue;. And more important, what does it have for implications, when using this over _structValue = Unsafe<T, TheStruct>(ref _value);?

Thanks for reading and I hope you can bring some light into this. :)


Solution

  • ref var structValue = ref _structValue;
    structValue = ref Unsafe.As<T, TheStruct>(ref _value); // WORKS: Why, and what implication does it have?
    

    Why I can assign Unsafe.As as ref, when defining ref var structValue = ref _structValue;

    Because you can reassign the reference of a ref local to another reference.

    This example will make things clear I hope:

    var foo = 42;
    var bar = 1;
    
    ref var refVar = ref foo;
    refVar++; // as if we wrote foo++
    Console.WriteLine(foo); // 43
    
    refVar = ref bar; // at this point we detach refVar as alias for foo
    
    refVar++; // as if we wrote bar++
    Console.WriteLine(foo); // still 43
    Console.WriteLine(bar); // 2
    

    So to your original question:

    ref var structValue = ref _structValue;
    structValue = ref Unsafe.As<T, TheStruct>(ref _value); // WORKS: Why, and what implication does it have?
    

    the second line won't assign to the field _structvalue the reference that Unsafe.As returns. It will assign the reference to the local variable structValue.

    So when Test2 returns, you have actually done nothing to the field _structValue. In Test1 for the working part you actually did copy the value which was there at the moment from _value:

    _structValue = Unsafe<T, TheStruct>(ref _value);
    

    Future changes to _value won't be reflected in _structValue.

    You may want to look in to ref structs available from .NET 7, where you can have not only ref local variables, but ref fields such as:

    ref TheStruct _structValue;
    

    If you did what you wanted, however,

    _structValue = ref Unsafe.As<T, TheStruct>(ref _value); 
    

    the compiler will issue this error:

    CS8374 Cannot ref-assign 'Unsafe.As<T, TheStruct>(ref _value)' to '_structValue' because 'Unsafe.As<T, TheStruct>(ref _value)' has a narrower escape scope than '_structValue'.
    

    which I think is a bit strange, because it is a field in the same struct...but that's maybe another question.