Я углубился в эту проблему. Вот ответ от Мэдс Торгерсен из Microsoft.
От Мадс:
Это немного прискорбно, но по замыслу. В
dynrecord2.r.real = newval; // change value
Значение dynrecord2.r упаковывается, что означает копирование в его собственный объект кучи. Эта копия модифицируется, а не оригинал, который вы впоследствии тестируете.
Это является следствием самого «локального» поведения C # dynamic. Подумайте об утверждении, подобном приведенному выше - есть два основных способа, которыми мы могли бы атаковать это:
1) Понимать во время компиляции, что происходит что-то динамическое, и по существу перемещать весь оператор, чтобы быть связанным во время выполнения
2) Связывать отдельные операции во время выполнения, когда их составляющие являются динамическими, возвращая что-то динамическое, что, в свою очередь, может привести к тому, что вещи будут связаны во время выполнения
В C # мы пошли с последним, который имеет приятную композицию и позволяет легко описывать динамику в терминах системы типов, но имеет некоторые недостатки - такие как, например, упаковка результирующих типов значений.
Итак, то, что вы видите, является результатом этого выбора дизайна.
Я еще раз взглянул на MSIL. По сути это занимает
dynrecord2.r.real = newval;
и превращает его в:
Real temp = dynrecord2.r;
temp.real = newval;
Если dynrecord2.r является классом, он просто копирует дескриптор, чтобы изменение влияло на внутреннее поле. Если dynrecord2.r является структурой, копия создается, и изменение не влияет на оригинал.
Я оставлю это на усмотрение читателя, чтобы решить, является ли это ошибкой или функцией.
Макс