C#4.0の新機能。 パート3:汎化の共分散

C#2.0でジェネリックが登場したとき、それらはこの言語の最高の機能の1つになりました。 C#1.0で強く型付けされたコレクションクラスを作成したことがある人は、どれだけ私たちの生活を簡素化し、コードの量を減らしたかを知っています。 唯一の問題は、ジェネリック型が通常の型に有効な継承ルールに従っていないことです。



この記事で使用する2つの単純なクラスの定義から始めましょう。
public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .



  1. public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .



  2. public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .



  3. public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .



  4. public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .



  5. public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .



  6. public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .



public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .



これはまだ具体的なことは何もしていない古典的なクラス階層ですが、今は必要ありません。 ここで、ダミークラスを定義します。これは、任意の型のコンテナになります。




  1. パブリック インターフェイス IContainer <T>
  2. {
  3. T GetItem();
  4. }
  5. パブリック クラスコンテナ<T>:IContainer <T>
  6. {
  7. プライベート Tアイテム。
  8. パブリックコンテナ(Tアイテム)
  9. {
  10. this .item = item;
  11. }
  12. パブリック T GetItem()
  13. {
  14. 返品アイテム;
  15. }
  16. }
*このソースコードは、 ソースコードハイライターで強調表示されました。
階層とコンテナがあります。 では、C#-3.0の現在のバージョンではできないことを見てみましょう。




  1. static void Main( string [] args)
  2. {
  3. IContainer <Shape> list = GetList();
  4. }
  5. public static IContainer <Shape> GetList()
  6. {
  7. 新しいコンテナ<円>( 新しい円())を返します。
  8. }
*このソースコードは、 ソースコードハイライターで強調表示されました。
戻り値の型がIContainer <Shape>として定義されているGetListメソッドがあり、Container <Circle>を返します。 CircleはShapeを継承し、ContainerはIContainerインターフェイスを実装しているため、これが機能するように見えるかもしれません。 しかし、あなたはそれを推測しました、C#3.0はこれに対応していません。



C#4.0では、これを機能させる方法があります-IContainerインターフェイス定義のtypeパラメーターにoutキーワードを追加するだけです(C#4.0の共分散はインターフェイスとデリゲートに制限されていることに注意してください)。




  1. パブリック インターフェイス IContainer < out T>
  2. {
  3. T GetItem();
  4. }
*このソースコードは、 ソースコードハイライターで強調表示されました。
この構造は、型Tが共変であることをコンパイラーに伝えます。つまり、すべてのIContainer <T>は、Tと同等またはそれ以上の型を受け入れます。上記のように、戻り型はIContainer <Shape>でしたが、インターフェイスへの出力パラメータを使用すると、IContainer <Circle>を簡単に返すことができるので、なぜoutキーワードを使用することにしましたか? これは、型パラメーターを共変として定義すると、インターフェイスからこの型のみを返すことができるためです。 たとえば、次のような構造は許可されていません。




  1. パブリック インターフェイス IContainer < out T>
  2. {
  3. void SetItem(T item);
  4. T GetItem();
  5. }
*このソースコードは、 ソースコードハイライターで強調表示されました。
しかし、なぜ機能しないのでしょうか? 答えは実際には非常に簡単です-型の安全性。 私たちがしたことの結果を見てみましょう:




  1. static void Main( string [] args)
  2. {
  3. IContainer <Shape> container = new Container <Circle>();
  4. SetItem(コンテナ);
  5. }
  6. public static void SetItem(IContainer <Shape>コンテナー)
  7. {
  8. container.SetItem( new Square()); //ブーム!!!
  9. }
*このソースコードは、 ソースコードハイライターで強調表示されました。
Tは共変であるため、Container <Circle>をIContainer <Shape>型の変数に割り当て、さらにIContainer <Shape>型のパラメーターを受け取る静的SetItemメソッドに渡し、このパラメーターを取得して追加しようとします。タイプSquareの変数。 すべてが正しいようです-パラメータタイプはIContainer <Shape>であり、これによりSquareを追加する権利が与えられます。 間違っています。 Circleを保持するコンテナにSquareを追加しようとすると、この式は「爆発」します。 したがって、outキーワードを使用すると、共分散は一方向のみに制限されます。



おそらく、これらすべてがCLR 4.0でどのように実装されているのか疑問に思っているでしょうか? 実装の必要はありません。 ジェネリック型はCLR 2.0で機能し、既に同様の動作を許可していました。 C#は型の安全性を維持しようとしたため、これを行うことはできませんでしたが、CLRはこれを一度に処理します。 そして、少し注意してください-C#の配列はこの動作を許可するので、試してみてください。 この記事をお楽しみいただき、このシリーズの新しい記事が間もなく登場することを願っています!



記事翻訳 C#4.0新機能パート3-汎用共分散



私の ブログ からのクロスポスト






All Articles