为什么可以使用反射改变只读字段的值,而不能改变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
值的工作方式,它们的值被复制到使用它们的每个程序集中。而readonly
字段是在运行时求值的。
原因是常量在编译时被替换为它的值。但是只读字段不是。您可以在声明和/或在该类的构造函数中为只读字段设置值。我希望这能回答你的问题。