Почему CLR позволяет изменять упакованные неизменяемые типы значений?

У меня есть ситуация, когда у меня есть простой неизменяемый тип значения:

public struct ImmutableStruct
{
    private readonly string _name;

    public ImmutableStruct( string name )
    {
        _name = name;
    }

    public string Name
    {
        get { return _name; }
    }
}

Когда я помещаю в коробку экземпляр этого типа значения, я обычно ожидаю, что что бы это ни было то, что я упаковал, выйдет так же, когда я сделаю распаковку. К моему большому удивлению, это не так. Используя Reflection, кто-то может легко изменить память моего ящика, повторно инициализировав содержащиеся в нем данные:

class Program
{
    static void Main( string[] args )
    {
        object a = new ImmutableStruct( Guid.NewGuid().ToString() );

        PrintBox( a );
        MutateTheBox( a );
        PrintBox( a );;
    }

    private static void PrintBox( object a )
    {
        Console.WriteLine( String.Format( "Whats in the box: {0} :: {1}", ((ImmutableStruct)a).Name, a.GetType() ) );
    }

    private static void MutateTheBox( object a )
    {
        var ctor = typeof( ImmutableStruct ).GetConstructors().Single();
        ctor.Invoke( a, new object[] { Guid.NewGuid().ToString() } );
    }
}

Пример вывода:

Что в коробке: 013b50a4-451e-4ae8-b0ba-73bdcb0dd612 :: ConsoleApplication1.ImmutableStruct Что в коробке: 176380e4-d8d8-4b8e-a85e-c29d7f09acd0 :: ConsoleApplication1.ImmutableStruct

(На самом деле в MSDN есть небольшая подсказка, указывающая, что это предполагаемое поведение)

Почему CLR позволяет изменять упакованные (неизменяемые) типы значений таким тонким способом? Я знаю, что только для чтения не является гарантией, и я знаю, что при использовании «традиционного» отражения экземпляр значения можно легко изменить. Такое поведение становится проблемой, когда ссылка на коробку копируется, а мутации появляются в неожиданных местах.

Одна вещь, о которой я думаю, заключается в том, что это позволяет вообще использовать Reflection для типов значений, поскольку API System.Reflection работает только с объектом . Но Reflection распадается при использовании типов значений Nullable <> (они упаковываются в null, если у них нет Value). Что здесь за история?

12
задан Johannes Rudolph 22 August 2011 в 16:51
поделиться