views:

241

answers:

4

Hi

I have the following code inside main method:

List<Rectangle> rects = new List<Rectangle>();

for (int i = 0; i < 5; i++)
{
    rects.Add(new Rectangle(1, 1, 1, 1));
}

foreach (Rectangle item in rects)
{
    Console.WriteLine(item);
}

rects[1].Inflate(100, 100);

foreach (Rectangle item in rects)
{
    Console.WriteLine(item);
}

In the presented code rects[1] remains unchanged. This is because indexer (which still is a special method) returned the copy of the rectangle structure. In this case the elements were located on heap. The indexer returned the new copy of the element by placing the new copy on the stack (since the Rectangle is a value type).

So far so good...

Later I have created an array of Rectangle structures in the Program class:

 Rectangle[] rect = new Rectangle[] { new Rectangle(1, 1, 1, 1), new Rectangle(1, 1, 1, 1) };

In the main method:

Program p = new Program();

p.rect[1].Inflate(100, 100);

foreach (var item in p.rect)
{
    Console.WriteLine(item);
}

I expected the indexer of rect array also to return copy of the rectangle structure, but this time the original element (which was also located on the heap) was changed.

Why is that? Does the array indexer works in a different way?

Kind Regards PK

A: 

Yes. An array indexer represents the variable to be changed directly. An indexer is basically a method with an index parameter with syntactic sugar applied to it.

The same difference applies to fields and properties.

If you were returning a reference type from the indexer, it wouldn't make much a difference since it would refer to the same object nevertheless. But when you return a struct instance (or any value type for that matter), the value is copied when returned and you are trying to modify the copy, which is pretty much useless.

Mehrdad Afshari
and for precisely the same reason, you can't do things like `myWindowsForm.Location.X++;`.
Mehrdad Afshari
A: 

The "indexer" for arrays isn't really an indexer, as far as I know. Arrays (or rather the Array type) cannot be treated as a normal type in the .NET Framework because of its special role. (To give another example, array items are covariant, which is helpful in many cases put leads to abnormalities compared to other types). It simply references the nth element of the array, so that the object it returns is in fact the actual object stored in the array. Just treat the two concepts separately. The fact that they have the same syntax isn't really relevant here - there's some similarity in functionality, but not much.

Noldorin
+2  A: 

From this you conclude that the indexer on a list is a method call that places the value of a value type on the stack when returning from a method and that the array indexer is actually translated in code to the calculation of an offset from the start of the array and is a direct memory reference.

tvanfosson
Well said.You can see the same difference more clearly if you try in your examples { rects[1].X = 100; } - wouldn't compile, I believe, as opposed to { p.rect[1].X = 100; } which would compile and work.
configurator
A: 

As stated before, Arrays are special things in the CLI. They have no indexer. "Indexing" into the array is directly converted to the appropriate ldelem CIL instruction.

Thomas Danecker