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

本文关键字:不能 能改变 const 字段 可以使 改变 反射 读字段 为什么 | 更新日期: 2023-09-27 18:04:12

为什么可以使用反射修改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";
}

我已经读了这个答案,所以我理解它是允许打破规则的readonly,但为什么不为const在这种情况下?我肯定有一个很好的理由,但我想不出是什么。

—Edit—

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

我的意思是,即使const的值在二进制文件中是"硬编码的",也不能修改它的原因是框架本身的技术限制,因为"它已经设置好了",或者只是一个设计决策。我看不出有什么理由不能像readonly那样在某处修改const

—Edit 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)

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

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

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