Considering the following code:
namespace MyApp
{
using System;
using System.Collections.ObjectModel;
class Program
{
static void Main(string[] args)
{
var col = new MyCollection();
col.Add(new MyItem { Enum = MyEnum.Second });
col.Add(new MyItem { Enum = MyEnum.First });
var item = col[0];
Console.WriteLine("1) Null ? {0}", item == null);
item = col[MyEnum.Second];
Console.WriteLine("2) Null ? {0}", item == null);
Console.ReadKey();
}
}
class MyItem { public MyEnum Enum { get; set; } }
class MyCollection : Collection<MyItem>
{
public MyItem this[MyEnum val]
{
get
{
foreach (var item in this) { if (item.Enum == val) return item; }
return null;
}
}
}
enum MyEnum
{
Default = 0,
First,
Second
}
}
I was surprised to see the following result:
1) Null ? True
2) Null ? False
My first expectation was that because I was passing an int
, the default indexer should be used, and the first call should have succeeded.
Instead, it seems that the overload expecting an enum
is always called (even when casting 0 as int), and the test fails.
- Can someone explain this behavior to me?
- And give a workaround to maintain two indexers: one by index, and one for the enum?
EDIT : A workaround seems to be casting the collection as Collection, see this answer.
So:
- Why does the compiler choose the most "complex" overload instead of the most obvious one (despite the fact it's an inherited one)? Is the indexer considered a native int method? (but without a warning on the fact that you hide the parent indexer)
Explanation
With this code we are facing two problems:
- The 0 value is always convertible to any enum.
- The runtime always starts by checking the bottom class before digging in inheritance, so the enum indexer is chosen.
For more precise (and better formulated) answers, see the following links:
- original answer by James Michael Hare
- sum up by Eric Lippert