Recipe 3.24. Initializing a Constant Field at RuntimeproblemA field marked as const can be initialized only at compile time. You need to initialize a field to a valid value at runtime, not at compile time. This field must then act as if it were a constant field for the rest of the application's life. SolutionYou have two choices when declaring a constant value in your code. 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 must 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… } DiscussionA 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.
The following code shows how to use an instance 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 AlsoSee the "const" and "readonly" keywords in the MSDN documentation. |