Why can't I initialize readonly variables in a initializer?
The initializer is just syntactic sugar. When you write:
new Foo { bar=0; };
(Which, by the way, is a syntax error and should be this...)
new Foo { bar=0 }
what's actually happening is:
var x = new Foo();
x.bar = 0;
Since the property is read-only, that second statement is invalid.
Edit: Based on your edit, the question is a little unclear. A readonly
property is, by design, not settable. It's built at object construction. This is enforced by both the compiler and the runtime. (Admittedly, I haven't tested the latter, since it would take some trickery to get around the former.)
Keep in mind that there are two stages of "compilation." It's enforced when compiling the C# code into IL code, and it's enforced when compiling the IL code into machine code.
It's not a technical limit of the CLR, and it's working exactly as it should, given the explicit readonly
declaration. After the object is constructed, you can't set a readonly
property.
Allowing a readonly
to be set in an initializer introduces contradictions and complications that can't be enforced at compile-time. I imagine the restriction is to avoid ambiguity. The big key is compile-time validation.
Imagine this:
class Foo
{
public readonly int bar;
Foo () {
// compiler can ensure that bar is set in an invoked ctor
bar = 0;
}
}
// compiler COULD know that `bar` was set in ctor
// and therefore this is invalid
new Foo { bar = 0; }
Now, consider:
class Foo
{
public readonly int bar;
Foo () {
// imagine case where bar not set in ctor
}
}
// compiler COULD know that `bar` is not bound yet
// therefore, this COULD be valid
new Foo { bar = 0; }
// but this COULD be proved to never be valid
new Foo();
Imagine that both of the above cases are unified (say, "by compiler magic"), however, enter in generics:
T G<T> () where T : new
{
// What in heck should happen *at compile time*?
// (Consider both cases above.)
// What happens if T (Foo) changes to include/not-include setting the
// readonly variable in the ctor?
// Consider intermediate code that invokes G<Foo>() and this other
// code is NOT recompiled even though Foo is--
// Yet a binary incompatibility has been added!
// No thanks!
return new T();
}
G<Foo>();
I believe the cases I have outlined show some complications of using a "dynamic" readonly
approach and, at the end of the day, I believe it is merely a chosen language restriction (compilers implement languages) to enforce/allow compile-time validation.
Since readonly
variables must be initialized in constructor, and property initializers execute after the construction of object, that is not valid.