我有一个情况,我有一个简单的、不可变的值类型:
public struct ImmutableStruct
{
private readonly string _name;
public ImmutableStruct( string name )
{
_name = name;
}
public string Name
{
get { return _name; }
}
}
当我封装一个值类型的实例时,我通常希望在解封后,封装的内容保持不变。但令我惊讶的是,情况并非如此。使用反射可以轻易地通过重新初始化其中包含的数据来修改封箱内存。
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会以这种微妙的方式允许更改装箱的(不可变的)值类型?我知道只读并不能保证,我也知道使用“传统”的反射可以轻松地更改值实例。当引用到Box复制到意外的地方时,这种行为就成为了一个问题。
我所想的一件事是,这使得在值类型上使用反射成为可能——因为System.Reflection API仅使用object
。但是使用Nullable<>
值类型时,反射会遇到问题(如果它们没有值,则会被装箱为null)。这里的情况是怎样的呢?
ConstructorInfo
。 - leppie