8

Why is it possible to modify the value of a readonly field using reflection but not the value of 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";
}

I've read this answer so I understand it's allowed to break the rules for readonly but why not for const in that case? I'm sure there's a good reason but I can't figure out what it could be.

-- Edit --

When I take a look at the code above using ildasm, the value of the readonly field is set at compile time. Not on the field itself unlike for the const but in the constructor of the class. So I'm not sure to get why one can be "overwritten" but not the other one.

What I mean is, even if the value of the const is "hard-coded" in the binary, is the reason for not being able to modifiy it a technical limitation in the framework itself because "it's already set" or just a design decision. I don't see any reason why there could not be some "magic" somewhere modifying the const as it's doing it for the readonly.

-- Edit 2 --

To add up to the accepted answer, there is also this other answer which is quite interesting. What I didn't get in the first place when asking this question is that the value behind the const is really replaced anywhere it is used in the code. With this declaration:

public const string Foo = "Hello";

writing later

Console.WriteLine(Foo);

is equivalent to writing

Console.WriteLine("Hello");

Indeed my code

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

is replaced in IL by

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)
Community
  • 1
  • 1
Guillaume
  • 1,782
  • 1
  • 25
  • 42
  • 1
    See also: [What is the difference between const and readonly?](http://stackoverflow.com/questions/55984/what-is-the-difference-between-const-and-readonly) – Cody Gray - on strike Mar 17 '13 at 08:14
  • @CodyGray Thanks for your link. I didn't know about the way the memory was handled for `const` and the impact it could have on other assemblies. Modifying the value of a `const` could have unknown impact on other assemblies I guess. – Guillaume Mar 18 '13 at 09:55

2 Answers2

9

Because const fields are 'set' at compile time, i.e. the compiler replaces the const with the given value during compilation. As a result of the way const values work, their values are copied into every assembly that uses them. Whereas readonly fields are evaluated at runtime.

ColinE
  • 68,894
  • 15
  • 164
  • 232
  • When I take a look at the code in my question in ildasm, the value of the `readonly` field is also set at compile time. Not on the field itself, unlike for the `const` but in the constructor anyway. So I'm still not sure to get why one can be "overwritten" but not the other one. – Guillaume Mar 17 '13 at 08:03
  • @Guillaume can you please update your question to include your IL observations for both const and readonly. – ColinE Mar 17 '13 at 08:04
  • @Guillaume what you might be observing is a compiler optimisation in this case that makes your readonly appear the same as your const declaration. You can't infer too much from the compiled code! – ColinE Mar 17 '13 at 08:05
2

Reason is that the constants are replaced with it value during the compile time itself. But readonly fields are not. You can set a value for a readonly fields either at declaraiotn and/or at that class's constructor. I Hope this answers your question.

PSL
  • 123,204
  • 21
  • 253
  • 243