Просто, чтобы добавить IL
веселья в обсуждение:
Заголовок метода Main
выглядит следующим образом:
method private hidebysig static void
Main() cil managed
{
.maxstack 3
.locals init (
[0] class MyClass a,
[1] class MyClass b
)
Оператор a.x = (a=b);
переводится в следующий IL
:
IL_000d: ldloc.0 // a
IL_000e: ldloc.1 // b
IL_000f: dup
IL_0010: stloc.0 // a
IL_0011: stfld class MyClass::x
Первые две инструкции загружаются ( ldlo c .0 , ldlo c .1 ) в ссылки стека оценки, хранящиеся в Переменные a
и b
, давайте назовем их aRef
и bRef
, чтобы получить следующее состояние стека оценки:
bRef
aRef
Инструкция dup
копирует текущее самое верхнее значение в стеке оценки, а затем помещает копию в стек оценки:
bRef
bRef
aRef
stlo c .0 выталкивает текущее значение из вершины оценки стек и сохраняет его в списке локальных переменных с индексом 0 (a
переменная установлена на bRef
), оставляя стек в следующем состоянии:
bRef
aRef
И, наконец, stfld
извлекает из стека значение (bRef
) и ссылку на объект / указатель (aRef
). Значение поля в объекте (aRef.x
) заменяется предоставленным значением (bRef
).
Все это приводит к поведению, описанному в сообщении, с обеими переменными (a
и b
), указывающими на bRef
, где bRef.x
является нулевым, а aRef.x
указывает на bRef
, что можно проверить с помощью дополнительной переменной, содержащей aRef
как предложено @ Magnetron .