为什么可以使用反射更改只读字段的值而不是const的值?

时间:2013-03-17 07:57:47

标签: c# reflection

为什么可以使用反射而不是readonly的值修改const字段的值?

class Program
{
    static void Main(string[] args)
    {
        Foobar foobar = new Foobar();
        Console.WriteLine(foobar.foo);                   // Outputs "Hello"
        Console.WriteLine(Foobar.bar);                   // Outputs "Hello"

        var field = foobar.GetType().GetField("foo");
        field.SetValue(foobar, "World");                // Ok
        field = foobar.GetType().GetField("bar");
        field.SetValue(foobar, "World");                // Throws FieldAccessException

        Console.ReadKey();
    }
}

public class Foobar
{
    public readonly string foo = "Hello";
    public const string bar = "Hello";
}

我已阅读this answer,因此我理解允许违反readonly的规则,但在这种情况下为什么不const?我确信这是一个很好的理由,但我无法弄清楚它可能是什么。

- 编辑 -

当我使用ildasm查看上面的代码时,readonly字段的值在编译时设置。与const不同,不是在字段本身,而是在类的构造函数中。所以我不确定为什么可以“覆盖”而不是另一个。

我的意思是,即使const的值在二进制文件中是“硬编码的”,也是无法修改它在框架本身的技术限制的原因,因为“它已经是设定“或只是一个设计决定。我没有看到为什么在const正在为readonly修改const时某些“魔法”没有任何理由。

- 编辑2 -

要添加到已接受的答案,还有this other answer非常有趣。在提出这个问题时我首先没有得到的是public const string Foo = "Hello"; 背后的值是真正替换它在代码中使用的任何地方。有了这个声明:

Console.WriteLine(Foo);

稍后写作

Console.WriteLine("Hello");

相当于写作

Console.WriteLine(foobar.foo);
Console.WriteLine(Foobar.bar);

确实是我的代码

IL_0008:  ldfld      string ConsoleApplication3.Foobar::foo
IL_000d:  call       void [mscorlib]System.Console::WriteLine(string)
IL_0012:  nop
IL_0013:  ldstr      "Hello"
IL_0018:  call       void [mscorlib]System.Console::WriteLine(string)

在IL中替换为

{{1}}

2 个答案:

答案 0 :(得分:9)

因为const字段在编译时被“设置”,即编译器在编译期间用编译器替换const。由于const值的工作方式,它们的值将被复制到使用它们的每个程序集中。而readonly字段在运行时进行评估。

答案 1 :(得分:2)

原因是在编译时自身将常量替换为常量。但只读字段不是。您可以在declaraiotn和/或该类的构造函数中为只读字段设置值。我希望这能回答你的问题。