以下、自分用のメモです。
C# 4.0では、ジェネリックインターフェースに対して、「共変性(Covariance)」と「反変性(Contravariance)」
が適用されるようになりました。
言葉がややこしいので、実際にソースを見た方が分かりやすいです。
共変性とは、簡単に言うと、子のクラスの型を親クラスの型に変換出来ること。
string str = "gsf_zero1"; object obj = str;
これは、利用している人が多いと思います。
C# 4.0より、ジェネリックインターフェースに対して、共変性を利用出来るようになりました。
IEnumerable<string> strings = new []{ "gsf_zero1", "gsf_zero2" }; IEnumerable<object> objects = strings;
共変性は、型引数に対して「out」キーワードを利用して、表明します。
.NET 4.0では、IEnumerable
public interface IEnumerable<out T> : IEnumerable { ... }
「out」キーワードが付与されている場合、その型引数は「出力方向」にしか利用できなくなります。
出力方向とは、つまり戻り値としてのみ利用するという意味です。
この制限を付ける事で、共変性が実現されます。
反変性は、共変性の逆です。
簡単に言うと、親クラスの型を子クラスの型に設定出来ること。
class Parent { ... } class Child : Parent { ... } delegate Parent SampleDelegate(); Child SampleMethod() { ... } static void Main() { SampleDelegate theDelegate = SampleMethod; theDelegate(); }
C# 4.0より、ジェネリックインターフェースに対して、反変性を利用出来るようになりました。
Action<object> objAction = x => Console.WriteLine(x); Action<string> strAction = objAction; strAction("gsf_zero1");
反変性は、型引数に対して「in」キーワードを利用して、表明します。
.NET 4.0では、Action
public delegate void Action<in T>(T obj)
「in」キーワードが付与されている場合、その型引数は「入力方向」にしか利用できなくなります。
入力方向とは、つまり引数としてのみ利用するという意味です。
この制限を付ける事で、反変性が実現されます。
以下、サンプルです。
#region CovarianceSamples-01 public class CovarianceSamples01 : IExecutable { public void Execute() { // // Covariance(共変性)は、簡単に言うと、子のオブジェクトを親の型として扱う事。 // // 例: // string str = "gsf_zero1"; // object obj = str; // // C# 4.0では、この概念をジェネリックインターフェースに対して適用できるようになった。 // 共変性を表明するには、型引数を定義する際に、「out」キーワードを設定する。 // // .NET 4.0では、IEnumerable<T>は以下のように定義されている。 // public interface IEnumerable<out T> : IEnumerable { ... } // // 「out」キーワードは、この型引数を「出力方向」にしか利用しないことを表明している。 // つまり、「out」キーワードが付与されるとTを戻り値などの出力値にしか利用できなくなる。 // (outを指定している状態で、入力方向、つまりメソッドの引数などにTを設定しようとすると // コンパイルエラーが発生する。) // // 出力方向にしか利用しないので、子の型(つまり狭義の型)を親の型(つまり広義の型)に // 設定しても、問題ない。 // 「内部の型はstringであるが、実際に値を取り出す際には親の型で受け取るので問題ない」 // // Contravariance(反変性)は、この逆を行うものとなる。 // IEnumerable<string> strings = new []{ "gsf_zero1", "gsf_zero2" }; IEnumerable<object> objects = strings; foreach (var obj in objects) { Console.WriteLine("VALUE={0}, TYPE={1}", obj, obj.GetType().Name); } } } #endregion #region ContravarianceSamples-01 public class ContravarianceSamples01 : IExecutable { public void Execute() { // // Contravariance(反変性)は、簡単に言うと、親のオブジェクトを子の型として扱う事。 // (共変性の逆です。) // // 例: // class Parent { ... } // class Child : Parent { ... } // // delegate Parent SampleDelegate(); // // Child SampleMethod() { ... } // // // ここで反変性が発生している。 // SampleDelegate theDelegate = SampleMethod; // // C# 4.0では、この概念をジェネリックインターフェースに対して適用できるようになった。 // 共変性を表明するには、型引数を定義する際に、「in」キーワードを設定する。 // // .NET 4.0では、Action<T>は以下のように定義されている。 // public delegate void Action<in T>(T obj) // // 「in」キーワードは、この型引数を「入力方向」にしか利用しないことを表明している。 // つまり、「in」キーワードが付与されるとTを引数などの入力値にしか利用できなくなる。 // (inを指定している状態で、出力方向、つまりメソッドの戻り値などにTを設定しようとすると // コンパイルエラーが発生する。) // // 入力方向にしか利用しないので、親の型(つまり広義の型)を子の型(つまり狭義の型)に // 設定しても、問題ない。 // 「外部の型はstringであるが、実際にデータが渡される際、内部の引数の型はobjectなので問題ない」 // // 例: // Action<object> objAction = x => Console.WriteLine(x); // Action<string> strAction = objAction; // // strAction("gsf_zero1"); // // 上記の例だと、objActionをstrActionに設定している。つまり親クラスの型で定義されているAction<object>を // 子のクラスのAction<string>に設定している。 // その後、strAction("gsf_zero1")としているので、外部から渡された値はstring型である。 // しかし、objActionの引数の型は、親クラスであるobject型なので問題なく動作する。 // (親クラスに定義されている振る舞いしか利用できないため。) // // Covariance(共変性)は、この逆を行うものとなる。 // Action<object> objAction = x => Console.WriteLine(x); Action<string> strAction = objAction; strAction("gsf_zero1"); } } #endregion
便利だな〜と思う反面、私自身で言えば、自分で定義したクラスに共変性とか反変性を付与することは
ほぼ無いと思ってたり・・・・。