为什么可以使用反射而不是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}}
答案 0 :(得分:9)
因为const
字段在编译时被“设置”,即编译器在编译期间用编译器替换const
。由于const
值的工作方式,它们的值将被复制到使用它们的每个程序集中。而readonly
字段在运行时进行评估。
答案 1 :(得分:2)
原因是在编译时自身将常量替换为常量。但只读字段不是。您可以在declaraiotn和/或该类的构造函数中为只读字段设置值。我希望这能回答你的问题。