本文介绍了更改了 .NET 4.5 中 string.Empty(或 System.String::Empty)的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简短版本:

C# 代码

typeof(string).GetField("Empty").SetValue(null, "Hello world!");
Console.WriteLine(string.Empty);

在编译和运行时,在 .NET 4.0 及更早版本下给出输出 "Hello world!",但在 .NET 4.5 和 .NET 4.5 下给出 "".1.

when compiled and run, gives output "Hello world!" under .NET version 4.0 and earlier, but gives "" under .NET 4.5 and .NET 4.5.1.

如何像这样忽略对字段的写入,或者谁重置了该字段?

How can a write to a field be ignored like that, or, who resets this field?

更长的版本:

我一直不明白为什么 string.Empty 字段(也称为 [mscorlib]System.String::Empty)不是 constcode>(又名.literal),请参阅为什么 String.Empty 不是常量?".这意味着,例如,在 C# 中,我们不能在以下情况下使用 string.Empty:

I have never really understood why the string.Empty field (also known as [mscorlib]System.String::Empty) is not const (aka. literal), see "Why isn't String.Empty a constant?". This means that, for example, in C# we can't use string.Empty in the following situations:

  • case string.Empty:
  • 形式的 switch 语句中
  • 作为可选参数的默认值,如void M(string x = string.Empty) { }
  • 应用属性时,如[SomeAttribute(string.Empty)]
  • 其他需要编译时常量的情况

这对众所周知的宗教战争"是否使用 string.Empty"" 有影响,请参阅在 C# 中,我应该使用 string.Empty 还是 String.Empty 或"来初始化字符串?".

which has implications to the well-known "religious war" over whether to use string.Empty or "", see "In C#, should I use string.Empty or String.Empty or "" to intitialize a string?".

几年前,我通过反射将 Empty 设置为其他一些字符串实例来娱乐自己,看看 BCL 的多少部分因此开始表现异常.这是相当多的.并且 Empty 引用的更改似乎在应用程序的整个生命周期中都存在.现在,前几天我试图重复那个小特技,但后来使用了 .NET 4.5 机器,我再也做不到了.

A couple of years ago I amused myself by setting Empty to some other string instance through reflection, and see how many parts of the BCL started behaving strangely because of it. It was quite many. And the change of the Empty reference seemed to persist for the complete life of the application. Now, the other day I tried to repeat that little stunt, but then using a .NET 4.5 machine, and I couldn't do it anymore.

(注意!如果你的机器上有 .NET 4.5,可能你的 PowerShell 仍然使用旧版本的 .NET(仅适用于 Windows 7 或更旧版本,其中 PowerShell 有PowerShell 2.0 之后没有更新),所以尝试将 [String].GetField("Empty").SetValue($null, "Hello world!") 复制粘贴到 PowerShell 中以查看更改此引用的一些影响.)

(NB! If you have .NET 4.5 on your machine, probably your PowerShell still uses an older version of .NET ( only true for Windows 7 or older where PowerShell has not been updated beyond PowerShell 2.0), so try copy-pasting [String].GetField("Empty").SetValue($null, "Hello world!") into PowerShell to see some effects of changing this reference.)

当我试图寻找原因时,我偶然发现了一个有趣的话题.NET 4.5 中出现这个 FatalExecutionEngineError 的原因是什么测试版?".在该问题的公认答案中,是否注意到通过 4.0 版,System.String 有一个静态构造函数 .cctor,其中字段 Empty> 已设置(在 C# 源代码中,当然,这可能只是一个字段初始值设定项),而在 4.5 中不存在静态构造函数.在两个版本中,字段本身看起来都一样:

When I tried to search for a reason for this, I stumbled upon the interesting thread "What's the cause of this FatalExecutionEngineError in .NET 4.5 beta?". In the accepted answer to that question, is it noted that through version 4.0, System.String had a static constructor .cctor in which the field Empty was set (in the C# source, that would probably just be a field initializer, of course), while in 4.5 no static constructor exists. In both versions, the field itself looks the same:

.field public static initonly string Empty

(如 IL DASM 所见).

(as seen with IL DASM).

除了 String::Empty 之外,似乎没有其他字段受到影响.例如,我试验了 System.Diagnostics.Debugger::DefaultCategory.这种情况看起来很相似:一个密封的类,包含一个 static readonly (static initonly) 类型为 string 的字段.但在这种情况下,通过反射改变值(引用)工作正常.

No other fields than String::Empty seems to be affected. As an example, I experimented with System.Diagnostics.Debugger::DefaultCategory. This case seems analogous: A sealed class containing a static readonly (static initonly) field of type string. But in this case it works fine to change the value (reference) through reflection.

回到问题:

从技术上讲,当我设置字段时,Empty 似乎没有改变(在 4.5 中)怎么可能?我已经验证 C# 编译器不会欺骗"读取,它输出 IL 如下:

How is it possible, technically, that Empty doesn't seem to change (in 4.5) when I set the field? I have verified that the C# compiler does not "cheat" with the read, it outputs IL like:

ldsfld     string [mscorlib]System.String::Empty

所以应该读取实际字段.

so the actual field ought to be read.

在对我的问题进行悬赏后请注意写入操作(这肯定需要反射,因为该字段是 readonly(又名 initonly 在 IL)) 实际上按预期工作.异常的是读取操作.如果你用反射读取,如在 typeof(string).GetField("Empty").GetValue(null) 中,一切正常(即看到值的变化).请参阅下面的评论.

Edit after bounty was put on my question: Note that the write operation (which needs reflection for sure, since the field is readonly (a.k.a. initonly in the IL)) actually works as expected. It is the read operation which is anomalous. If you read with reflection, as in typeof(string).GetField("Empty").GetValue(null), everything is normal (i.e. the change of value is seen). See comments below.

所以更好的问题是:为什么这个新版本的框架在读取这个特定字段时会作弊?

So the better question is: Why does this new version of the framework cheat when it reads this particular field?

推荐答案

不同之处在于新版本 .NET 的 JIT,它显然通过内联引用优化了对 String.Empty 的引用到特定的 String 实例,而不是加载存储在 Empty 字段中的值.这在 ECMA-335 Partition I §8.6.1.2 中 init-only 约束的定义下是合理的,可以解释为 String.Empty 的值String 类初始化后字段不会改变.

The difference lies in the JIT for the new release of .NET, which apparently optimizes references to String.Empty by inlining a reference to a particular String instance rather than load the value stored in the Empty field. This is justified under the definition of the init-only constraint in ECMA-335 Partition I §8.6.1.2, which can be interpreted to mean the value of the String.Empty field will not change after the String class is initialized.

这篇关于更改了 .NET 4.5 中 string.Empty(或 System.String::Empty)的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-19 14:32