У меня есть ситуация, когда у меня есть простой, неизменный тип значения:
public struct ImmutableStruct
{
private readonly string _name;
public ImmutableStruct( string name )
{
_name = name;
}
public string Name
{
get { return _name; }
}
}
Когда я упаковываю экземпляр этого типа значения, я обычно ожидаю, что все, что я в штучной упаковке, выйдетто же самое, когда я делаю unbox.К моему большому удивлению, это не тот случай.Используя 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 допускает мутирование коробочных (неизменяемых) типов значений таким тонким способом? Я знаю, что readonly не является гарантией, и я знаю, что использование "традиционного" отражения для экземпляра значенияможет быть легко видоизменен.Такое поведение становится проблемой, когда ссылка на блок копируется и мутации обнаруживаются в неожиданных местах.
Единственное, о чем я хочу сказать, это то, что это вообще позволяет использовать Reflection для типов значений - поскольку API-интерфейс System.Reflection работает только с object
.Но Reflection разбивается на части при использовании Nullable<>
типов значений (они упаковываются в нуль, если у них нет значения).Что за история здесь?