Это из-за базовых инструкций IL.
Программа выполняет эту последовательность инструкций, чтобы получить нужный элемент:
Загрузить адрес в стек.
Загрузить смещение в стек.
Добавьте их.
Считать значение по этому адресу памяти.
Если объект находится в куче, а затем перемещается из-за сборки мусора до шага 4, то адрес, загруженный с шага 1, больше не будет действительным. Чтобы защититься от этого, вам нужно сначала закрепить объект в памяти.
(Тот факт, что вы обращаетесь к структуре с помощью указателя this
, означает, что вы понятия не имеете, находится ли структура в куче или в стеке, поэтому вы должны закрепить ее на всякий случай, если она находится в куче .)
Второй пример работает, потому что он копирует структуру в стек , поэтому копия никогда не будет перемещаться, поэтому адрес всегда будет действительным.
Почему такая же проблема не возникает с другими типами полей? Поскольку их смещение известно в время компиляции , в то время как индекс массива известен в время выполнения , поэтому JIT может генерировать код, который всегда будет правильно обращаться к полям.