Greetings to all my blog readers!
In C# the readonly keyword can be very handy. It allows one to define fields that are assigned at the point of instance creation (in the instance constructor), or inline, at the point of declaration. Once assigned, the fields remain immutable and cannot be reassigned by the type consumers. Of course, another type instance can be created, and it may have different values assigned to its readonly fields (e.g. if the constructor uses input parameters to perform the assignment).
Unlike constants, readonly fields are initialized at run time. This can be useful to avoid the application assembly versioning problem that may arise from using constants. Great! Let’s go ahead and create some readonly arrays!
In the below code I create an int array myIntArray, which is marked with readonly keyword and assigned in the constructor of IntArray class. In the Main method I am subsequently modifying the first value in the array and print it out to show that it has changed. The code compiles and prints out 6, 2, 3, 4 and 5. But wait! Didn’t I just say that readonly fields cannot be modified by the type consumers? What is going on here?
using System; using System.Linq; namespace ReadOnlyRef { sealed internal class IntArray { internal IntArray() { myIntArray[0]= 1; myIntArray[1] = 2; myIntArray[2] = 3; myIntArray[3] = 4; myIntArray[4] = 5; } internal readonly int [] myIntArray= new int[5]; } class EntryPoint { static void Main(string[] args) { IntArray ia = new IntArray(); ia.myIntArray[0] = 6; for (int i = 0; i < ia.myIntArray.Count(); i++) Console.WriteLine(ia.myIntArray[i].ToString()); Console.ReadLine(); } } }
Well, in the above code we got exactly what we were asking for – the int array is readonly, that is, its reference is readonly. So, if we declare another instance of IntArray and attempt the following reassignment, it won’t work:
IntArray ia2 = new IntArray(); ia2.myIntArray = ia.myIntArray;
The above won’t compile and the error is: ‘A readonly field cannot be assigned to (except in a constructor or a variable initializer).’
When the readonly field is a reference type, it is its reference and not the object that becomes readonly. To make the object readonly, I can suggest doing one of the following two:
1. The easiest and the most elegant way is to make the readonly field private, assign it once in the constructor, and make it accessible via the readonly property:
internal IntArray() { myIntArray[0]= 1; myIntArray[1] = 2; myIntArray[2] = 3; myIntArray[3] = 4; myIntArray[4] = 5; } internal int[] MyIntArray { get { return myIntArray; } } private readonly int [] myIntArray= new int[5];
…
… in the type consumer…
IntArray ia = new IntArray(); int[] arrayCopy = ia.MyIntArray; for (int i = 0; i < arrayCopy.Count(); i++) Console.WriteLine(arrayCopy[i].ToString());
2. You can achieve reference field immutability through implementing readonly interfaces. Check out this post if you want to know more. With this design, the type consumer would need to make a conscious choice to ensure immutability.