【问题标题】:Unable to change readonly field with reflection无法使用反射更改只读字段
【发布时间】:2013-08-06 05:18:07
【问题描述】:

所以,我在摆弄和学习反思时遇到了一个奇怪的问题。 我正在尝试更改一个私有的只读字段,如下所示:

public class A 
{
    private static readonly int x;

    public int X
    {
        get { return x; }
    }
}

static void Main(string[] args)
{
    A obj = new A();
    Type objType = typeof(A);

    Console.WriteLine(obj.X);

    FieldInfo objField = objType.GetField("x", BindingFlags.Static | BindingFlags.NonPublic);
    objField.SetValue(null, 100);

    Console.WriteLine(obj.X);

    Console.ReadLine();
}

如果我按照上面的方式运行程序,那么每次都会将 0 打印到控制台。 但是,如果我注释掉第一个打印,那么第二个将写出预期的 100。

谁能解释一下这里发生了什么?谢谢!

编辑:奇怪的是,它似乎在 Visual Studio 2012 中有效,但在 2010 年无效。据我发现,两者的设置相同。

编辑 2:使用平台目标 x64 构建时有效,而不是 x86。猜猜新问题是:为什么会这样?

编辑3:在反汇编中比较了x64和x86版本; x86 版本中似乎有一些内联。

编辑 4:好吧,想想我已经弄清楚发生了什么,有点。我不认为 A 类中的属性被内联是问题。我相信当在 main 方法中第二次读取属性时,属性调用被优化掉(支持字段应该是只读的,值应该相同)并且旧值被重用。至少这是我的“理论”。

【问题讨论】:

  • 那是因为您第一次打印时调用它,为其分配默认值,即 0,之后始终为 0,如果您注释掉第一个,那么您将分配值 100 和然后打印它的值。
  • 打电话给 Marc Gravel,打电话给 Marc Gravel
  • 由于该字段被标记为readonly,JIT 可能会内联X getter。
  • @terrybozzio 是的,看起来合乎逻辑,但根据我在此站点上其他线程中所读到的内容,您应该能够通过使用反射来更改只读字段。除非我误解了什么
  • 我无法在我的机器上重现您的问题。也就是说,您提供的代码实际上按预期工作。只是为了证明我没有带你走花园小路:i.imgur.com/ZR1U7vW.png

标签: c# reflection readonly


【解决方案1】:

JIT 正在内联 getter:

使用 getter 上的 MethodImplAttribute 建议 JIT 不内联该属性。这不会阻止 x 的值被内联,但删除 readonly 会产生所需的结果。

public class A
{
    private static int x;

    public int X
    {
        [MethodImpl(MethodImplOptions.NoInlining)]
        get { return x; }
    }
}

现在输出将是:

0
100

见:Does C# inline properties?

一种简单的解决方法是使用可为空的整数 (int?) 值类型。

public class A 
{
    private static readonly int? x;

    public int X
    {
        get
        {
            return x ?? 0;
        }
    }
}

x 不会被内联,因为 CLR 需要检查 x 的值是否是对 int 的有效强制转换。

【讨论】:

  • 当您编译调试版本而不是发布版本时,即使在进行了更改之后,您也会看到不同的结果。不过,我确信它一定与抖动有关。
  • 我仍然得到相同的结果;都来自 MarshalByRefObject 和属性。
  • 在问题的编辑段落中指定的条件下,上面的代码仍然重现了“问题”,因此NoInlining 属性中的get 访问器似乎还不够。
  • 静态字段是readonly,因此 JIT 内联了属性调用。解决方案是不要更改标记为 readonly 的静态字段,因为无法控制 JIT 行为。
猜你喜欢
  • 1970-01-01
  • 2012-02-03
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 2019-06-20
  • 2014-10-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多