C# で一般的な分散を使用するには、次の条件をすべて満たす必要があります。
- C# 4 を使用する
- 変化する型は、ジェネリック インターフェイスまたはジェネリック デリゲートである必要があります
- 型パラメーターは、"in" (反変) または "out" (共変) とマークする必要があります。
- 型パラメーターの注釈は、可能なすべての操作で型安全であることが証明されている型を生成する必要があります。
- 「ソース」タイプの引数と「宛先」タイプの引数には、それらの間で ID または参照変換が必要です。
プログラムは条件 1 と条件 5 を満たしていますが、条件 2、3、または 4 を満たしていません。
条件 4 に違反するものが必要なため、必要な分散を取得する方法はありません。条件 4 を除くすべての条件を満たした場合に何が起こるかを見てください。
// Suppose this declaration were legal:
interface IMyCollection<out T>
{
List<T> Items { get; set; }
}
class Animal {}
class AnimalCollection : IMyCollection<Animal>
{
public List<Animal> { get; set; }
}
class Giraffe : Animal {}
class GiraffeCollection : IMyCollection<Giraffe>
{
public List<Giraffe> { get; set; }
}
static class X
{
public IMyCollection<Animal> GetThing()
{
// IMyCollection is covariant in T, so this is legal.
return new GiraffeCollection();
}
}
class Tiger : Animal {}
...
IMyCollection<Animal> animals = X.GetThing();
// GetThing() actually returns a GiraffeCollection
animals.Items = new List<Animal>() { new Tiger(); }
また、キリン コレクションには、トラを含む動物のリストが含まれるようになりました。
バリアンスを使用する場合は、タイプセーフにする必要があります。コンパイラはバリアンス アノテーションがタイプセーフであると判断できないため、IMyCollection の宣言を拒否します。
これをどのようにしてタイプセーフにするかを見てみましょう。問題は、アイテムがインターフェイスを介して書き込み可能であることです。
interface IMyCollection<out T>
{
IEnumerable<T> Items { get; }
}
class Animal {}
class AnimalCollection : IMyCollection<Animal>
{
public IEnumerable<Animal> { get { yield return new Tiger(); } }
}
class Giraffe : Animal {}
class GiraffeCollection : IMyCollection<Giraffe>
{
public IEnumerable<Giraffe> { get { yield return new Giraffe(); } }
}
static class X
{
public IMyCollection<Animal> GetThing()
{
return new GiraffeCollection();
}
}
class Tiger : Animal {}
...
MyCollection<Animal> animals = X.GetThing();
// GetThing() actually returns a GiraffeCollection
foreach(Animal animal in animals.Items) { ... }
// Items yields a giraffe, which is an animal
完全にタイプセーフです。これは合法的な C# 4 プログラムです。
C# 4 での共分散と反分散の設計の詳細に興味がある場合は、このテーマに関する多数の記事を読むことを検討してください。ここでそれらを見つけることができます:
http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/
これらは最新のものから古いものへの順序でリストされていることに注意してください。下から始めます。
特に、インターフェースのアノテーションがいつ有効であるかを判断するためのルールに興味がある場合は、この記事を参照してください (いくつかのエラーを発見して修正したので、この会話ができてよかったです)。
http://blogs.msdn.com/b/ericlippert/archive/2009/12/03/exact-rules-for-variance-validity.aspx