短版:
# C代码
1 2
| typeof(string).GetField("Empty").SetValue(null, "Hello world!");
Console .WriteLine(string.Empty); |
当编译和运行,使输出"Hello world!"在.NET 4.0版和更早的,但给""在.NET 4.5 4.5.1和.NET。
如何写这样的一场被忽略,或复位,世卫组织这场?
时间版本:
我从来没有真正理解为什么《string.Empty场(又称为[mscorlib]System.String::Empty)是不const(又名。literal湖),"为什么不是空字符串常数。A?"这意味着,例如,我们可以在"T C # string.Empty在以下情况使用:
- 在一switch语句的case string.Empty:
- 作为可选参数的默认值在void M(string x = string.Empty) { },样
- 工人们样的属性,[SomeAttribute(string.Empty)]
- 其他的情况下,在编译的时间常数是必需的
它已经影响到我们的宗教是"是否被使用过或""string.Empty湖",# C,我要使用字符串或空或空字符串。。。。。。。。""初始化字符串?"。。。。。。。
几年前,一个由Emptyi安德烈自己设置一些其他的字符串或通过反射,和湖多少部件的Bcl behaving strangely开始因为它。这是相当多的。参考和变化的Empty滑稽的坚持完整的生命的应用。现在,我想那天那个小特技重复序列,然后用.NET 4.5机器,我不能这样做了。
(NB!如果你有在你的.NET 4.5 PowerShell安静的机器,可能是使用旧版本的.NET,以尝试复制到pasting [String].GetField("Empty").SetValue($null,"Hello world!")PowerShell的一些影响,这一变化的参考湖)。
当我试图搜索这一原因,我坐在线程"的兴趣是什么,因为这fatalexecutionengineerror。4.5测试版?"在公认的答案。这是它的两个通孔,这是System.String4.0版本,有一个静态构造函数.cctor其中场Empty什么集(C #源,这会是一场很及时的初始化时,当然),而在静态构造函数4.5 NO的存在。在两个版本,它看起来是一样的:场
1
| .field public static initonly string Empty |
作为一个dasm湖IL)。
没有其他的场String::Empty似乎比的影响。作为一个例子,一个System.Diagnostics.Debugger::DefaultCategoryexperimented。这是类似案例:a密封舱(含static readonlystatic initonly)string场型。但在这个案例它精细改变值(参考)通过反射。
回到这个问题:
技术上的知识,这是可能的,Empty似乎没有变化(4.5)设置在一场吗?我已经给了# C编译器不读"作弊"的酒店,它的输出细胞样:
1
| ldsfld string [mscorlib]System.String::Empty |
因此,实际的场应该读取。
编辑后的赏金是我的问题:注意上的写操作(当然这需要反思的是,从一场readonly(又名initonly在IL))是为喜欢的作品。它是anomalous是读取操作。如果你读与反思,在typeof(string).GetField("Empty").GetValue(null)(IU,一切是正常的价值是变化的湖泊)。评论下面的湖。
因此,更好的问题是:为什么这个新版本的框架,特别是当它读这场作弊?
- 如果这和单声道的情况相同,也会很有趣。
- 我认为这更适合程序员。
- 虽然编译器没有作弊,但是clr很有可能作弊。
- 当您右转并使用反射打印出string.Empty的值时会发生什么?
- @柯克沃尔真是一个伟大的评论!我不知道为什么我不想这么做。当我思考阅读的时候,新的价值观也受到尊重。这意味着Console.WriteLine(typeof(string).GetField("Empty").GetValue(null));在4.5中打印"Hello world!"。
- @雷南,对程序员来说不是很适合。OP提出了一个具体的可回答问题,原则上有一个正确答案。所以非常适合。
- 可能是邪恶代码混淆的副本,它是如何编译的?
- @本沃伊特:另一个问题与这个问题几乎没有任何联系;唯一的松散联系是"通过反射访问string.Empty"。另一个问题是,为什么系统程序集中的readonly字段可以写入;这一个问题是,为什么以及如何在特定版本的.NET中拒绝某些基于反射的写入操作。
- @本沃伊特正如O.R.Mapper所说,这不是另一个问题的复制品。我的问题是:为什么像我们在另一个问题中看到的那样的程序的行为在.NET 4和.NET 4.5之间发生了变化?
- @Jeppe:这两个问题都是"使用反射修改initonly字段来破坏.NET全局不变量的含义是什么?"标记一个副本并不意味着问题的措辞相同,这意味着如果你阅读其他问题和答案,你就会找到这个问题的答案。
- @Benvoigt很好,但是仅仅是因为你在声称这个线程是那个线程的副本之后,在另一个线程中扩展了你自己的答案。我可以合并这样的问题,但由于SmartCaveman在这条线上花了50分的悬赏金,这可能不是结束它的正确时机。
- 我添加到那个答案中的所有内容都已经在其他答案的评论中了。所以现在很容易找到并解释得更清楚,但是信息已经为任何人找到了。
区别在于.NET新版本的JIT,它显然通过引入对特定String实例的引用而不是加载存储在Empty字段中的值来优化对String.Empty的引用。根据ECMA-335第I部分第8.6.1.2节中仅init约束的定义,这是合理的,可以解释为在初始化String类后,String.Empty字段的值不会改变。
- 另外,在.NET 4中,System.String类有一个分配给字段的静态构造函数(方法.cctor())。在.NET 4.5及更高版本中,情况不再如此。我想知道为什么一个通读反射(看起来不是"欺骗")不把这个字段看作是null。
我没有答案,只是暗示一下。
我在String::Empty和System.Diagnostics.Debugger::DefaultCategory之间看到的唯一区别是,第一个标记为__DynamicallyInvokableAttribute。
我不知道这个未记录的属性的含义。关于这个属性的一个问题已经被问到了:什么是"dynamicallyInvokable"属性?
我只能假设这个属性被运行时捕获以进行缓存?
因为它可以。
这些系统定义的initonly字段的值是.NET运行时的全局不变量。如果这些不变量被破坏,就不再有任何关于行为的保证。
在C++中,我们可能会有一个规则将此指定为未定义的行为。在.NET中,它也是未定义的行为,仅仅是因为没有任何规则说明当System.String.Empty.Length > 0时会发生什么。.net和c的所有层的整个规范描述了当System.String.Empty.Length == 0和一大堆不变量也成立时的行为。
有关运行时和含义之间不同的优化的详细信息,请参阅
- 要求反射API覆盖System.String.Empty会有什么影响?