共変性
共変性とは、要は暗黙の型変換を容易にするための機能である。参照型の型 A が型 B へ暗黙の型変換が可能な場合に、ジェネリックインターフェースの共変な型パラメータ(out 付き型パラメータ)に型 A を指定した If<A> は同じインタフェースの If<B> へ暗黙の型変換が可能になる。
※out が付いた型パラメータは出力(メソッドの戻り値、読取専用プロパティの型)にしか使えなくなる。
出力用の out 引数には使えない。→理由
インタフェースの他に、デリゲートの型パラメータも共変にすることができる。
[共変性のサンプル]
class A : B { } // B はクラスまたはインターフェース
static class TestCovariance
{
static void Main(string[] args)
{
// 暗黙の型変換
A a = new A();
B b = a;
// 共変のインターフェース IEnumerable<out T>
IEnumerable<A> aList = new List<A>();
IEnumerable<B> bList = aList;
// 共変のデリゲート Func<out TResult>
Func<A> aFunc = () => new A();
Func<B> bFunc = aFunc;
}
}
配列の共変性
C# 及び VB の配列は共変性がある。共変性があるのに、不変な型パラメータを持つ ICollection<T> と IList<T> を実装している。
※普通のコードでは実装できないため、実行時に提供するという特殊なやり方で。
これはジェネリック(.NET 2.0~)が配列(.NET 1.0~)より後に導入されたためでもあるのだが、そもそもイミュータブルでない配列を共変にしているのがおかしな話なのである。
MSDN にも書いてある話なのだが、配列を暗黙の型変換することでタイプセーフではなくなる。
例えば、次のコードはコンパイルできるが、実行時エラー ArrayTypeMismatchException が発生する。
static class TestCovariance
{
static void Main(string[] args)
{
string[] strings = {"1"};
object[] objects = strings;
objects[0] = 1; // ここで例外発生
}
}
IReadOnlyList<T>
上記の気持ち悪さを解決するためかは分からないが、.NET 4.5 からイミュータブルな配列を扱える IReadOnlyList<T> が導入されている。
static class TestCovariance
{
static void Main(string[] args)
{
// Array.AsReadOnly の戻り値は ReadOnlyCollection<T>
// このメソッドは .NET 2.0 からあったが IReadOnlyList<T> が存在しなかった
IReadOnlyList<string> strings = Array.AsReadOnly(new[]{"1"});
IReadOnlyList<object> objects = strings;
objects[0] = 1; // コンパイル NG
}
}
.NET 4.0 までの資産は IList<T> や ICollection<T> 前提で作ってあることが多いので、今更導入されてもという気がするのだが、よりタイプセーフに実装できるということで・・・
0 件のコメント:
コメントを投稿