[解決済み] C#の共変量インターフェースと共変量インターフェースの理解
質問
C#の教科書を読んでいて、このような記述を目にしたのですが、文脈が分からないためか、なかなか理解することができません。
これらが何であり、何のために有用であるかについての簡潔な良い説明はそこにありますか?
分かりやすくするために編集しました。
共変量インターフェイスです。
interface IBibble<out T>
.
.
反変化体インターフェース.
interface IBibble<in T>
.
.
どのように解決するのですか?
とは
<out T>
とすることで、インターフェース参照を1つ上の階層として扱うことができます。
とは
<in T>
とすることで、インターフェース参照を階層的に1つ下の階層として扱うことができます。
もう少し英語で説明しましょう。
動物園から動物のリストを取得し、それを処理するつもりだとします。(動物園の) すべての動物には名前と一意の ID があります。ある動物は哺乳類、ある動物は爬虫類、ある動物は両生類、ある動物は魚類などですが、それらはすべて動物です。
ということは、(さまざまな種類の動物を含む)動物のリストがあれば、すべての動物に名前があると言えるわけで、当然、すべての動物の名前を取得しても問題ないでしょう。
しかし、魚だけのリストがあり、それらを動物のように扱う必要がある場合はどうでしょうか。直感的にはうまくいくはずですが、C# 3.0 以前では、このコード片はコンパイルされないでしょう。
IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>
この理由は、コンパイラがあなたの意図することを "知らない" ため、あるいは
ができます。
を知らないからです。コンパイラが知っている限りでは、このコレクションに
IEnumerable<T>
を通してオブジェクトをリストに戻す方法があるかもしれません。そうすれば、魚だけを含むことになっているコレクションに、魚ではない動物を入れることができる可能性があります。
つまり、コンパイラはこれが許されないことを保証できないのです。
animals.Add(new Mammal("Zebra"));
つまり、コンパイラはあなたのコードのコンパイルを真っ向から拒否しているのです。これが共分散です。
共分散について見てみましょう。
私たちの動物園はすべての動物を扱えるので、魚も扱えるはずです。そこで、私たちの動物園に魚を加えてみましょう。
C# 3.0以前では、これはコンパイルされません。
List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>
fishes.Add(new Fish("Guppy"));
ここでは、コンパイラの
は
を返すにもかかわらず、このコード片を許可しています。
List<Animal>
を返すにもかかわらず、このコードを許可することができます。これは、魚がすべて動物であるためで、型をこれに変更するだけで、このようになります。
List<Animal> fishes = GetAccessToFishes();
fishes.Add(new Fish("Guppy"));
とすればうまくいくのですが、コンパイラはこれをやろうとしていないことを判断できないのです。
List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>
Fish firstFist = fishes[0];
リストは実際には動物のリストであるため、これは許されない。
つまり、contra-とco-varianceは、オブジェクト参照をどう扱うか、何をすることが許されるかということですね。
は
in
と
out
のキーワードは、特にインターフェイスをどちらか一方にマークします。このキーワードは
in
では、一般的な型(通常はT)を
入力
-の位置、つまりメソッドの引数や書き込み専用のプロパティに置くことができます。
とは
out
であれば、ジェネリックタイプを
出力
-の位置、つまりメソッドの戻り値、読み取り専用のプロパティ、メソッドのパラメータを出力することができます。
これにより、意図した通りのコードを実行できるようになります。
IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>
// since we can only get animals *out* of the collection, every fish is an animal
// so this is safe
List<T>
はTにインとアウトの両方があるので、co-variantでもcontra-variantでもなく、このようにオブジェクトを追加することができるインタフェースでした。
interface IWriteOnlyList<in T>
{
void Add(T value);
}
を使えば、こんなことができるようになります。
IWriteOnlyList<Fish> fishes = GetWriteAccessToAnimals(); // still returns
IWriteOnlyList<Animal>
fishes.Add(new Fish("Guppy")); <-- this is now safe
コンセプトを示すいくつかのビデオを紹介します。
以下はその例です。
namespace SO2719954
{
class Base { }
class Descendant : Base { }
interface IBibbleOut<out T> { }
interface IBibbleIn<in T> { }
class Program
{
static void Main(string[] args)
{
// We can do this since every Descendant is also a Base
// and there is no chance we can put Base objects into
// the returned object, since T is "out"
// We can not, however, put Base objects into b, since all
// Base objects might not be Descendant.
IBibbleOut<Base> b = GetOutDescendant();
// We can do this since every Descendant is also a Base
// and we can now put Descendant objects into Base
// We can not, however, retrieve Descendant objects out
// of d, since all Base objects might not be Descendant
IBibbleIn<Descendant> d = GetInBase();
}
static IBibbleOut<Descendant> GetOutDescendant()
{
return null;
}
static IBibbleIn<Base> GetInBase()
{
return null;
}
}
}
これらのマークがなければ、次のようにコンパイルできます。
public List<Descendant> GetDescendants() ...
List<Base> bases = GetDescendants();
bases.Add(new Base()); <-- uh-oh, we try to add a Base to a Descendant
またはこれ
public List<Base> GetBases() ...
List<Descendant> descendants = GetBases(); <-- uh-oh, we try to treat all Bases
as Descendants
関連
-
[解決済み】パディングが無効で、削除できない?
-
[解決済み】データが存在しないのに読み込もうとする試みが無効である
-
[解決済み] C#のStringとstringの違いは何ですか?
-
[解決済み] .NETでのdecimal, float, doubleの違い?
-
[解決済み] Microsoft Officeをインストールせずに、C#でExcel(.XLSおよび.XLSX)ファイルを作成するにはどうすればよいですか?
-
[解決済み] インターフェースと抽象クラスの違いは何ですか?
-
[解決済み] C#のconstとreadonlyの違いは何ですか?
-
[解決済み] async」と「await」の使い方とタイミング
-
[解決済み] C#のインターフェイス。暗黙の実装と明示の実装
-
[解決済み】TypeScriptのインターフェースと型について
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] [Solved] 1つ以上のエンティティで検証に失敗しました。詳細は'EntityValidationErrors'プロパティを参照してください [重複]。
-
[解決済み] エンティティタイプ ApplicationUser は、現在のコンテキストのモデルの一部ではありません。
-
[解決済み】コンパイルエラー「未割り当てのローカル変数を使用しています」が発生したのはなぜですか?
-
[解決済み】C#で四捨五入する方法
-
[解決済み】WebForms UnobtrusiveValidationModeは、jqueryのScriptResourceMappingを必要とする
-
解決済み] Critical error detected c0000374 - C++ dll returns pointer off allocated memory to C# [解決済み] Critical error detected c0000374 - C++ dll returns pointer off allocated memory to C#.
-
[解決済み] DBNullから他の型にオブジェクトをキャストすることができない
-
[解決済み】Sequence contains no matching element(シーケンスにマッチする要素がない
-
[解決済み] ...基礎となる接続は閉じられました。予期しないエラーが受信で発生しました
-
[解決済み】スレッド終了またはアプリケーションの要求により、I/O操作が中断されました。