The C# CLR contains a lot of nice collection classes. They are optimized for their individual use case. But from a common perspective they all have the same behavior we expect from a collection.
But there is one exception from this rule: the array class. The array was added to the CLR from the very beginning and you can think of this class as a built-in generic. The array class has many differences compared to the other CLR containers. One major difference is the indexer. The indexer of the array returns the element by reference and not by value like all other collections.
This difference may have a huge importance if you choose the right collection for your use case. Furthermore, if an array indexer is used without respect to the fact that it returns a reference, it can result in undefined behavior.
The following example shows the difference between an array indexer and a list indexer.
static void Main(string[] args) { var array = new[] { new MyStruct(42) }; var list = new List { new MyStruct(42) }; // array[0].mValue = 15; // list[0].mValue = 15; // error CS1612: Cannot modify the return value because it is not a variable array[0].SetValue(15); list[0].SetValue(15); Console.WriteLine("Array value: " + array[0].mValue); // output: 'Array value: 15' Console.WriteLine("List value: " + list[0].mValue); // output: 'List value: 42' } public struct MyStruct { public MyStruct(int x) { mValue = x; } public void SetValue(int x) { mValue = x; } public int mValue; }
The example shows two important differences. If we try to set a member value, we will become an error message in case of the list collection. This is because the list indexer returns a copy of the object. As we don’t create a variable to store this copy, we are not able to set the member value. If we instead use a member function to set the value, the function call will be successful in both cases. But it has a different behavior. The array value will be changed but the list value will not. That’s because the array indexer returns a reference to the array element. The member function is called for this element. The list indexer returns a copy of the element. The member function is called on this temporary object. So, the origin list member is not changed at all.
The different behavior of array indexer and list indexer isn’t an error in the C# CLR. On the contrary, it offers advantages because you can choose the right collection type according to your needs. So, you should keep this special behavior of the array indexer in mind and use it if you have according use cases.
Pingback: Ref Return and Ref Locals in C# 7 | coders corner