8

为什么可以readonly使用反射修改字段的值,但不能修改 a 的值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";
}

我已经阅读了这个答案,所以我知道它可以违反规则,readonly但为什么不const这样做呢?我敢肯定有一个很好的理由,但我无法弄清楚它可能是什么。

- 编辑 -

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

我的意思是,即使const二进制中的值是“硬编码”的,也是无法修改它的原因是框架本身的技术限制,因为“它已经设置”或只是一个设计决定。const我看不出有什么理由不能在某处修改readonly.

-- 编辑 2 --

加上已接受的答案,还有另一个非常有趣的答案。当我问这个问题时,我首先没有得到的是,后面的值const实际上代码中使用它的任何地方都被替换了。有了这个声明:

public const string Foo = "Hello";

稍后再写

Console.WriteLine(Foo);

相当于写

Console.WriteLine("Hello");

确实我的代码

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

在 IL 中被替换为

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)
4

2 回答 2

9

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

于 2013-03-17T08:00:32.410 回答
2

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

于 2013-03-17T08:03:26.830 回答