次のコードを検討します。
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
}
}
次の結果を見て驚いた。
1) Null ? True
2) Null ? False
私の最初の期待は、を渡していたint
ので、デフォルトのインデクサーを使用する必要があり、最初の呼び出しが成功するはずだったということでした。
代わりに、(0をintとしてキャストする場合でも)anを期待するオーバーロードenum
が常に呼び出され、テストが失敗するようです。
- 誰かが私にこの振る舞いを説明できますか?
- そして、2つのインデクサーを維持するための回避策を提供します。1つはインデックスごと、もう1つは列挙型用ですか。
編集:回避策はコレクションをコレクションとしてキャストしているようです。この回答を参照してください。
それで:
- コンパイラが最も明白なものではなく、最も「複雑な」オーバーロードを選択するのはなぜですか(継承されたものであるにもかかわらず)?インデクサーはネイティブintメソッドと見なされますか?(ただし、親インデクサーを非表示にするという事実についての警告はありません)
説明
このコードでは、2つの問題に直面しています。
- 0の値は、常に任意の列挙型に変換できます。
- ランタイムは、継承を掘り下げる前に常に最下位クラスをチェックすることから開始するため、列挙型インデクサーが選択されます。
より正確な(そしてより適切に定式化された)回答については、次のリンクを参照してください。