I know that List<T>
indexers, for example, resemble properties. Taken from msdn:
Indexers allow instances of a class or struct to be indexed just like arrays. Indexers resemble properties except that their accessors take parameters.
But I can't understand why the following happens:
int[] myArray = new int[0];
List<int> myList = new List<int>();
Interlocked.Increment(ref myArray[0]); // fine
Interlocked.Increment(ref myList[0]); //CS0206 A property or indexer may not be passed as an out or ref parameter
Aren't they supposed to work in the same way?
The indexer for List<T>
allows you to access the elements using properties (Methods) which makes it look like an array. You can't pass the generated method by ref
as if you wrote :
Interlocked.Increment(ref myList.GetItem.get_Item(0));
But accessing array's elements is not though an indexer. Accessing array elements is directly supported in the CLR. So array[i]
returns a variable that can be passed by ref
.
From C# Specs:
Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer element is not classified as a variable. Thus, it is not possible to pass an indexer element as a ref or out argument.
This is the indexer of List<T>
(which internally uses an array):
public T this[int index]
{
get
{ // some checks removed
return _items[index];
}
set { _items[index] = value;}
}
Accessing an array element generates this direct IL instruction:
IL_0014: ldelem.i4
Accessing a List's element through indexer generates this:
IL_001b: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Item(int32)
So, it is the same c# syntax. But the generated IL is completely different.
It's because indexers are properties and properties are ultimately functions. Because they are functions, they don't (necessarily) refer to a specific location in memory like an array index does. If the language DID allow it, developers would be able to easily break the reference and out semantics and it just wouldn't work right. Case in point:
public class Foo {
public int this[int x] {
get {
return 1;
}
set {
//meh. whatever.
}
}
}
If I pass Foo[0]
as an out parameter, what just happened? In this case, I don't even set anything. This completely breaks the semantics of ref
and out
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With