Присвойте значение по ссылке члену класса по ссылке

Что-то меня смущает, и я надеюсь, что вы сможете прояснить ситуацию.

// 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?
  }
}

Итак, мой вопрос в Test2: почему я могу назначить Unsafe.As в качестве ссылки при определении ref var structValue = ref _structValue;. И что еще более важно, какие последствия это будет иметь, если использовать это поверх _structValue = Unsafe<T, TheStruct>(ref _value);?

Спасибо за чтение, и я надеюсь, что вы сможете пролить свет на это. :)

🤔 А знаете ли вы, что...
C# был представлен в 2000 году и стал частью платформы .NET Framework.


53
1

Ответ:

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

Почему я могу назначить Unsafe.As в качестве ссылки при определении ref var structValue = ссылка _structValue;

Потому что вы можете переназначить ссылку ref local на другую ссылку.

Надеюсь, этот пример прояснит ситуацию:

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

Итак, к вашему первоначальному вопросу:

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

вторая строка не будет присваивать полю _structvalue ссылку, которая Unsafe.As возвращает. Он присвоит ссылку на локальную переменную structValue.

Поэтому, когда Test2 возвращается, вы фактически ничего не сделали с полем _structValue. В рабочей части Test1 вы фактически скопировали значение, которое было там в данный момент, из _value:

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

Будущие изменения _value не будут отражены в _structValue.

Возможно, вы захотите просмотреть ref-структуры, доступные в .NET 7, где вы можете иметь не только ref local переменные, но и ref fields такие как:

ref TheStruct _structValue;

Однако если бы вы сделали то, что хотели,

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

компилятор выдаст эту ошибку:

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'.

что мне кажется немного странным, потому что это поле в той же структуре... но это, возможно, другой вопрос.