42

誰かが、共変性、反変性、不変性、および反不変の簡単なC#の例を提供してくれませんか(そのようなものが存在する場合)。

これまでに見たすべてのサンプルは、何らかのオブジェクトを にキャストしただけでしたSystem.Object

4

3 に答える 3

89

誰かが、共変性、反変性、不変性、および反不変の簡単なC#の例を提供してくれませんか(そのようなものが存在する場合)。

「反不変性」の意味がわかりません。残りは簡単です。

共分散の例を次に示します。

void FeedTheAnimals(IEnumerable<Animal> animals) 
{ 
    foreach(Animal animal in animals)
        animal.Feed();
}
...
List<Giraffe> giraffes = ...;
FeedTheAnimals(giraffes);

IEnumerable<T>インターフェイスは共変です。Giraffe が Animal に変換可能であるという事実は、 が に変換可能であることを意味IEnumerable<Giraffe>IEnumerable<Animal>ます。List<Giraffe>このコードを実装しているためIEnumerable<Giraffe>、C# 4 で成功します。IEnumerable<T>C# 3 では共分散が機能しなかったため、C# 3 では失敗していたでしょう。

これは理にかなっているはずです。一連のキリンは一連の動物として扱うことができます。

反変性の例を次に示します。

void DoSomethingToAFrog(Action<Frog> action, Frog frog)
{
    action(frog);
}
...
Action<Animal> feed = animal=>{animal.Feed();}
DoSomethingToAFrog(feed, new Frog());

Action<T>デリゲートは反変です。Frog が Animal に変換可能であるという事実は、 が に変換可能であることを意味Action<Animal>Action<Frog>ます。この関係が共変の関係の反対方向であることに注意してください。それが「コントラ」バリアントである理由です。変換可能であるため、このコードは成功します。C# 3 では失敗していたでしょう。

これは理にかなっているはずです。アクションは、任意の動物を取ることができます。どのカエルにも対応できるアクションが必要です。また、どの動物にも対応できるアクションは、どのカエルにも対応できます。

不変性の例:

void ReadAndWrite(IList<Mammal> mammals)
{
    Mammal mammal = mammals[0];
    mammals[0] = new Tiger();
}

これに を渡すことはできますIList<Giraffe>か? いいえ、誰かがそれに Tiger を書き込もうとしていて、Tiger を Giraffes のリストに入れることはできないからです。IList<Animal>このものにを渡すことはできますか? いいえ、そこから哺乳類を読み取るため、動物のリストにはカエルが含まれる可能性があるためです。 IList<T>不変です。ありのままにしか使えません。

この機能の設計に関する追加の考えについては、私たちがどのように設計および構築したかについての一連の記事を参照してください。

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

于 2011-01-12T17:37:32.577 に答える
4

不変性(この文脈では)は、共分散と反分散の両方がないことです。したがって、反不変性という言葉には意味がありません。いずれinかとしてタグ付けされていない、またはout不変である型パラメーター。これは、この型パラメーターを使用して返すことができることを意味します。

共分散の良い例はIEnumerable<out T>IEnumerable<Derived>を に置き換えることができるためIEnumerable<Base>です。またはFunc<out T>、 type の値を返しますT
たとえば、犬は動物であるため、 anIEnumerable<Dog>は に変換できます。IEnumerable<Animal>

反分散には、消費するインターフェイスまたはデリゲートを使用できます。IComparer<in T>またはAction<in T>私の頭に浮かぶ。これらは type の変数を返すことはTなく、受け取るだけです。を受け取ることが期待される場所ならどこでも、Baseを渡すことができますDerived

それらを入力専用または出力専用の型パラメーターと考えると、IMO を理解しやすくなります。

また、不変条件という言葉は通常、型の分散と一緒には使用されませんが、クラスまたはメソッドの不変条件のコンテキストで使用され、保存されたプロパティを表します。不変条件と不変条件の違いについて説明しているこのスタックオーバーフロー スレッドを参照してください。

于 2011-01-12T14:47:17.390 に答える
2

ジェネリックの通常の使用を検討する場合、通常はインターフェイスを使用してオブジェクトを処理しますが、オブジェクトはクラスのインスタンスです。インターフェイスをインスタンス化することはできません。例として、文字列の単純なリストを使用します。

IList<string> strlist = new List<string>();

IList<>を直接使用するのではなく、 を使用することの利点を認識していると思いますList<>。これにより、制御の反転が可能にList<>なり、もはや を使用したくないが、LinkedList<>代わりに が必要になる場合があります。上記のコードは、インターフェイスとクラスの両方のジェネリック型が同じであるため、正常に機能します: string.

ただし、文字列のリストのリストを作成する場合は、もう少し複雑になる可能性があります。次の例を検討してください。

IList<IList<string>> strlists = new List<List<string>>();

IList<string>ジェネリック型の引数とが同じではないため、これは明らかにコンパイルされませList<string>ん。のように外側のリストを通常のクラスとして宣言したとしても、List<IList<string>>コンパイルされません - 型引数が一致しません。

ここで共分散が役立ちます。共分散により、この式の型引数として、より派生した型を使用できます。共変にするとIList<>、コンパイルして問題を修正するだけで済みます。残念ながら、IList<>共変ではありませんが、拡張するインターフェイスの 1 つは次のとおりです。

IEnumerable<IList<string>> strlists = new List<List<string>>();

このコードがコンパイルされるようになりました。型引数は上記と同じです。

于 2011-01-12T14:51:02.713 に答える