Recipe 3.25 Initializing a Constant Field at Runtime
Problem
A field marked as const
can be initialized only at compile time. You need to initialize a
field at runtime to a valid value, not at compile time. This field
must then act as if it were a constant field for the rest of the
application's life.
Solution
When declaring a constant value in your code, there are two choices.
You can use a readonly field or a
const field. Each has its own strengths and
weaknesses. However, if you need to initialize a constant field at
runtime, you should use a readonly field:
public class Foo
{
public readonly int bar;
public Foo( ) {}
public Foo(int constInitValue)
{
bar = constInitValue;
}
// Rest of class...
}
This is not possible using a const field. A
const field can be initialized only at compile
time:
public class Foo
{
public const int bar; // This line causes a compile-time error
public Foo( ) {}
public Foo(int constInitValue)
{
bar = constInitValue; // This line also causes a compile-time error
}
// Rest of class...
}
Discussion
A readonly field allows initialization to take
place only in the constructor at runtime, whereas a
const field must be initialized at compile time.
Therefore, implementing a readonly field is the
only way to allow a field that must be constant to be initialized at
runtime.
There are only two ways to initialize a readonly
field. The first is by adding an initializer to the field itself:
public readonly int bar = 100;
The second way is to initialize the readonly field
through a constructor. This is demonstrated through the code in the
Solution to this recipe.
If you look at the following class:
public class Foo
{
public readonly int x;
public const int y = 1;
public Foo( ) {}
public Foo(int roInitValue)
{
x = roInitValue;
}
// Rest of class...
}
You'll see it is compiled into the following IL:
.class public auto ansi beforefieldinit Foo
extends [mscorlib]System.Object
{
.field public static literal int32 y = int32(0x00000001) //<<-- const field
.field public initonly int32 x //<<-- readonly field
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 input) cil managed
{
// Code size 14 (0xe)
.maxstack 8
//001659: }
//001660: }
//001666: public class Foo
//001667: {
//001668: public readonly int x;
//001669: public const int y = 1;
//001670:
//001671: public Foo(int roInitValue)
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor( )
//001672: {
//001673: x = input;
IL_0006: ldarg.0
IL_0007: ldarg.1
IL_0008: stfld int32 Foo::x
//001674 }
IL_000d: ret
} // end of method Foo::.ctor
} // end of class Foo
Notice that a const field is compiled into a
static field, and a readonly
field is compiled into an instance field. Therefore, you need only a
class name to access a const field.
|
A common argument against using const fields is
that they do not version as well as readonly
fields. If you rebuild a component that defines a
const field, and the value of that
const changes in a later version, any other
components that were built against the old version
won't pick up the new value.
|
|
The following code shows how to use a readonly
field:
Foo obj1 = new Foo(100);
Console.WriteLine(obj1.bar);
Those two lines compile into the following IL:
IL_0013: ldc.i4 0xc8
IL_0018: newobj instance void Foo::.ctor(int32)
IL_001d: stloc.1
IL_001e: ldloc.1
IL_001f: ldfld int32 Foo::bar
Since the const field is already compiled into the
application as a static member field, only one
simple IL instruction is needed to use this const
field at any point in the application:
IL_0029: ldc.i4.1
Notice that the compiler compiled away the const
field and uses the value it was initialized to, which is
1. This is faster than using a
readonly field. However, const
fields are inflexible as far as versioning is concerned.
See Also
See the "const" and
"readonly" keywords in the MSDN
documentation.
|