精简版:
C# 代码
typeof(string).GetField("Empty").SetValue(null, "Hello world!");
Console.WriteLine(string.Empty);
编译和运行时,"Hello world!"
在 .NET 4.0 和更早版本下提供输出,但""
在 .NET 4.5 和 .NET 4.5.1 下提供。
如何像这样忽略对字段的写入,或者,谁重置了这个字段?
更长的版本:
我从来没有真正理解为什么该string.Empty
字段(也称为[mscorlib]System.String::Empty
)不是const
(aka. literal
),请参阅“为什么 String.Empty 不是常量? ”。这意味着,例如,在 C# 中我们不能string.Empty
在以下情况下使用:
- 在
switch
表格的声明中case string.Empty:
- 作为可选参数的默认值,例如
void M(string x = string.Empty) { }
- 应用属性时,例如
[SomeAttribute(string.Empty)]
- 需要编译时常量的其他情况
这对是否使用string.Empty
or的众所周知的“宗教战争”有影响""
,请参阅“在 C# 中,我应该使用 string.Empty 或 String.Empty 还是“”来初始化字符串? ”。
几年前,我Empty
通过反射设置到其他字符串实例来自娱自乐,看看 BCL 有多少部分因此而开始表现出奇怪的行为。这是相当多的。并且Empty
引用的更改似乎在应用程序的整个生命周期中持续存在。现在,前几天我试图重复那个小特技,但后来使用了 .NET 4.5 机器,我再也做不到了。
(注意!如果您的机器上有 .NET 4.5,可能您PowerShell
仍然使用旧版本的 .NET(编辑:仅适用于 PowerShell 未更新到 PowerShell 2.0 之后的 Windows 7 或更早版本),因此请尝试复制粘贴[String].GetField("Empty").SetValue($null, "Hello world!")
到PowerShell 查看更改此参考的一些效果。)
当我试图寻找原因时,我偶然发现了一个有趣的线程“ .NET 4.5 beta 中这个 FatalExecutionEngineError 的原因是什么? ”。在该问题的公认答案中,是否注意到通过版本 4.0,System.String
有一个静态构造函数.cctor
,其中设置了字段Empty
(在 C# 源代码中,这可能只是一个字段初始值设定项,当然),而在 4.5 中没有静态构造函数存在。在这两个版本中,字段本身看起来都一样:
.field public static initonly string Empty
(如 IL DASM 所示)。
String::Empty
似乎没有其他领域受到影响。例如,我尝试了System.Diagnostics.Debugger::DefaultCategory
. static readonly
这种情况看起来很相似:一个包含类型为( static initonly
)的密封类string
。但在这种情况下,通过反射更改值(参考)可以正常工作。
回到问题:
当我设置字段时,从技术上讲,这怎么可能Empty
没有改变(在 4.5 中)?我已经验证 C# 编译器不会“欺骗”读取,它输出 IL 如下:
ldsfld string [mscorlib]System.String::Empty
所以应该阅读实际的字段。
在对我的问题提出赏金后进行编辑:请注意,写入操作(肯定需要反射,因为该字段是readonly
(又名initonly
在 IL 中))实际上按预期工作。异常的是读操作。如果您通过反射阅读,如 中typeof(string).GetField("Empty").GetValue(null)
,一切正常(即看到值的变化)。请参阅下面的评论。
所以更好的问题是:为什么这个新版本的框架在读取这个特定字段时会作弊?