C# 为什么我可以在通过反射初始化字段之后重写静态只读字段?

C# 为什么我可以在通过反射初始化字段之后重写静态只读字段?,c#,reflection,C#,Reflection,正如我们所知,静态构造函数在第一次调用类时被调用 我们现在有一节课: static class Demo { private readonly static int _intValue; static Demo () { _intValue = 5; } } 当访问Demo时,将调用构造函数。值5将分配给\u intValue只读字段。之后,我们可以再次通过反射设置值: typeof (Demo) .GetField ("_ in

正如我们所知,静态构造函数在第一次调用类时被调用

我们现在有一节课:

static class Demo
{
    private readonly static int _intValue;

    static Demo ()
    {
        _intValue = 5;
    }
}
当访问
Demo
时,将调用构造函数。值5将分配给
\u intValue
只读字段。之后,我们可以再次通过反射设置值:

typeof (Demo)
  .GetField ("_ intValue", BindingFlags.Static | BindingFlags.NonPublic)
  .SetValue (null, 567);
为什么值
5
可以被覆盖?它不是已经在构造函数中初始化了吗?为什么不抛出
System.FieldAccessException

在NET Core 3.1中测试。完整示例

更新: 我找到了一种覆盖只读静态字段的方法,即使它被请求一次。它基于通过IL生成的方法。实际上,除了最初的问题——这个变通方法是如何工作的


反射已经打破了所有规则,包括可访问性和可变性;它的有效功能与
不安全
:就像
不安全
:如果出现问题,它是自己造成的,运行时会嘲笑你

请注意,在.NETCore中,运行时有时会阻止您这样做,因为如果您这样做,JIT优化将变得无效。但如果它不在这里:好的


注意:您过去可以通过反射更改
字符串。空的
。想象一下,结果如何:)

反射可以让您完成普通C代码甚至编译的IL代码通常无法完成的事情。例如,您可以从其声明类型之外调用私有方法。这似乎是一个类似的情况。@JoeSewell为什么你没有提到它作为一个答案?@JoeSewell我很感兴趣这个作业是如何详细完成的,它绕过了只读字段的限制?这是由环境提供的吗?为什么?或者这只是.NETCore的一个功能?例如,在NETFramework4.7中,通过反射根本找不到静态只读字段。这有什么关系吗?为了更好地理解问题中描述的行为,我不知道应该掌握什么。“在NetFramework4.7中,通过反射根本找不到静态只读字段”-这根本不是事实;在.net core 3.1中,您不能依赖于能够通过反射来创建静态只读字段,因为这会影响某些新的JIT优化(虽然这可能仅适用于ref类型(类),但对于设备化),下面是@MarcGravel的深层含义。“注意:您过去能够更改string.Empty”我在用我的计划程序替换TaskScheduler.Default时问了上述问题。当然,prod:)在这个简单的示例中,运行时允许覆盖只读静态字段,因为它正确地执行了JIT,我的理解正确吗?如果是这样的话,你能举个例子说明什么时候做不到吗?@KregHEk Sean Skelly已经给你链接了一个。也许这与您的示例是一种值类型有关,因此不需要(或可以)进行任何虚拟化。@KregHEk情不自禁;下面是另一个带有示例的链接,这次是。tl;dr:与仅具有静态只读字段相比,同时具有静态构造函数和静态只读字段会导致设置字段时IL的细微差异。但一定要通读一遍,并注意“beforefieldinit”。