在.NET 4.5中更改了string.Empty(或System.String :: Empty)的行为

简洁版本:

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 (又名literal ),请参阅“为什么不是String.Empty常量?”。 这意味着,例如,在C#中,我们不能在以下情况下使用string.Empty

  • case string.Empty:switch语句中case string.Empty:
  • 作为可选参数的默认值,如void M(string x = string.Empty) { }
  • 应用属性时,如[SomeAttribute(string.Empty)]
  • 其他需要编译时常量的情况
  • 这对有关是否使用string.Empty""的众所周知的“宗教战争”有影响,请参阅“在C#中,我应该使用string.Empty还是String.Empty或”“来intitialize一个字符串?”。

    几年前,我通过反思将Empty设置为其他字符串实例,并且看到BCL中有多少部分开始因为它而奇怪地行为。 这是相当多的。 Empty引用的变化似乎在应用程序的整个生命周期中持续存在。 现在,有一天我试着重复那个小小的特技,但是之后使用了一个.NET 4.5机器,而我再也无法做到了。

    (注意!如果你的机器上有.NET 4.5,可能你的PowerShell仍然使用旧版本的.NET,所以试试copy- [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 。 这种情况似乎是类似的:一个密封的类,包含一个类型为stringstatic readonlystatic initonly )字段。 但是在这种情况下,通过反射改变值(参考)可以很好地工作。

    回到问题:

    在技​​术上,如何在设置领域时Empty似乎没有改变(4.5)? 我已经验证了C#编译器不会“读”作弊,它会输出如下所示的IL:

    ldsfld     string [mscorlib]System.String::Empty
    

    所以应该阅读实际领域。


    在赏金之后编辑我的问题:请注意写操作(需要反射肯定,因为该字段是readonly (又名initonly在IL))实际上按预期工作。 这是异常的读取操作。 如果使用反射读取,如typeof(string).GetField("Empty").GetValue(null) ,则一切正常(即可看到值的变化)。 见下面的评论。

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


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


    也许我没有答案,只是暗示。

    我看到的唯一区别是String::EmptySystem.Diagnostics.Debugger::DefaultCategory是第一个用__DynamicallyInvokableAttribute标记的。

    我不知道这个未记录的属性的含义。 有关此属性的问题已在SO上提出:什么是__DynamicallyInvokable属性?

    我只能假设这个属性被运行时捕获到做一些缓存?


    因为它可以。

    这些系统定义的initonly字段的值是.NET运行时的全局不变量。 如果这些不变量被破坏,那么对于这种行为就不再有任何保证。

    在C ++中,我们可能会有一条规则将此指定为导致未定义的行为。 在.NET中,它也是未定义的行为,只是没有任何规则说明System.String.Empty.Length > 0时会发生什么。 .NET和C#的所有层的整个规范描述了当System.String.Empty.Length == 0和一大堆不变量都成立时的行为。

    有关在运行时间和含义之间有所不同的优化的更多信息,请参阅以下答案

  • 请求Reflection API重写System.String.Empty有什么含义?
  • 链接地址: http://www.djcxy.com/p/52737.html

    上一篇: Changed behavior of string.Empty (or System.String::Empty) in .NET 4.5

    下一篇: Is it better to call ToList() or ToArray() in LINQ queries?