【C#】ジェネリックの型引数にポリモーフィズムを適用したい！を実現する「共変性」「反変性」について

##型引数にポリモーフィズムを適用したい！

以下のようなことがしたいです。

```csharp
//これができるなら…
BaseClass @base = new SubClass();

//これができてもいいじゃん！
List<BaseClass> bases = new List<SubClass>();
```

しかし、悲しいかな↑のソースコードは`List<SubClass>`を`List<BaseClass>`に変換できませんと言われエラーとなります。

###`IEnumerable<T>`ならできるよ
ところが、`List<T>`ではなく`IEnumerable<T>`で受けると↑のようなことが可能になります。

```csharp
//IEnumerable<T>で受けるとできる。
IEnumerable<BaseClass> bases = new List<SubClass>();
```

###なんで`IEnumerable<T>`で受けるとできるの？

`List<T>`で受けるとダメだったのに、なんで`IEnumerable<T>`で受けると型引数にポリモーフィズムが適用できるのか？

それは、`IEnumerable<T>`が__読み取り専用インターフェイス__だからです。
`IEnumerable<T>`の定義を見てみましょう↓

```csharp
public interface IEnumerable<out T> : IEnumerable
{
	new IEnumerator<T> GetEnumerator ();
}
```
`IEnumerator<T>`を取得できるだけになっています。
じゃあ`IEnumerator<T>`の定義はというと…

```csharp
public interface IEnumerator<out T> : IEnumerator, IDisposable
{
	new T Current {
		[NullableContext (1)]
		get;
	}
}
```
`T`を取得できるだけののようです。
非ジェネリックバージョンの`IEnumerator`も継承していますが、型引数`T`が含まれないので無影響ですね。

同様に、`IReadOnlyList<T>`インターフェイスなんかも、名前の通り読み取り専用なので以下のように型引数にポリモーフィズムを適用できます。

```csharp
IReadOnlyList<BaseClass> bases = new List<SubClass>();
```

###なんで読み取り専用ならいいの？

`IEnumerable<T>`で受けると型引数にポリモーフィズムを適用できるのは、`IEnumerable<T>`が読み取り専用だからということがわかりました。
じゃあなんで読み取り専用だと型引数にポリモーフィズムが適用できるのか？

次の例を見てみましょう↓

####読み取り専用じゃないと…

```csharp
//もしこれが許されると…
List<BaseClass> bases = new List<SubClass>();

//これができてしまう！
//basesの実体はList<SubClass>なのに！！
bases.Add(new BaseClass());

//これもできてしまう！
//basesの実体はList<SubClass>なのに！！
bases.Add(new SubClass2());
```

インスタンスの実体としては`List<SubClass>`なのに、`BaseClass`のインスタンスや、`BaseClass`を継承した別の`SubClass2`のインスタンスを`Add`できてしまいます。

####読み取り専用だと…

```csharp
IReadOnlyList<BaseClass> bases = new List<SubClass>();

//読み取って出てくるオブジェクトはSubClassまたはそれを継承したクラスのインスタンス。
//でも別にBaseClassで受けるぶんには何も問題はない！
BaseClass @base = bases[0];
```

このように読み取るだけなら何も問題は生じません。


##「共変性」と「反変性」

このようにジェネリックの型引数にポリモーフィズムを適用できる機能を、「__ジェネリックの共変性__」といいます。
対して、その逆となる「__ジェネリックの反変性__」という機能も存在します。
※ちなみに、共変性・反変性はインターフェイス、またはデリゲートの型引数にのみ適用できます。

###ジェネリックの「共変性」

ジェネリックの共変性とは、ジェネリックの変数にその派生型の型引数をもつオブジェクトを代入できる性質のことです。
以下のように、型引数に`out`キーワードを付与することで、そのインターフェイスやデリゲートに共変性を付与することができます。

```csharp
interface ISomeInterface<out T> { }
```

先に紹介した`IEnumerable<T>`や`IReadOnlyList<T>`も、定義を見ると`out`修飾子がついていることが確認できます。

```csharp
public interface IEnumerable<out T> : IEnumerable
{
	new IEnumerator<T> GetEnumerator ();
}
```

```csharp
public interface IReadOnlyList<out T> : IEnumerable<T>, IEnumerable, IReadOnlyCollection<T>
{
	T this [int index] {
		get;
	}
}
```

`out`修飾子を付けると、そのインターフェイスには`T`型を入力するようなメンバ（`T`型プロパティの`get`アクセサー、`T`型の引数）を含めることはできなくなります。
そのため、コンパイラによって読み取り専用であることが保証されるため、安全に派生型の型引数をもつインスタンスを代入できるようになるわけです。

読み取り専用、つまり取り出し専用なので「out」と考えると分かりやすいですね。

###ジェネリックの「反変性」

対して、ジェネリックの反変性とは、派生元の型引数を持つオブジェクトを代入できる性質のことです。ちょうど共変性の対となる性質です。
以下のように、型引数に`in`キーワードを付与することで、そのインターフェイスやデリゲートに反変性を付与することができます。

```csharp
interface ISomeInterface<in T> { }
```
`out`修飾子が読み取り専用であるのに対して`in`修飾子ときたら想像つくと思いますが、これは「__書き込み専用__」であることを保証します。
`in`修飾子を付けると、以下のようなことが可能になります。

```csharp
ISomeInterface<BaseClass> @base;
ISomeInterface<SubClass> sub = @base; //ジェネリックの反変性により可能となるコード
```

派生型の型引数を持つ変数に、その派生元の型引数を持つインスタンスを代入できるようになります。
派生型にその派生元のインスタンスを代入するというのは、__とても不自然__に感じます。

なぜ、`in`修飾子をつけることによって「書き込み専用」が保証されると、このような事が可能になるのか？
以下のようなコードで考えてみます↓

```csharp
//このような書き込み専用インターフェイスがあるとします
interface ISomeInterface<in T>
{
    void SetValue(T value);
    T Prop { set; }
}
//SomeClassが実装しているとします
class SomeClass<T> : ISomeInterface<T>
{
    //…省略
}
```

####書き込み専用だと…



```csharp
//SubClass型の型引数をもつのインターフェイスに、BaseClass型の型引数を持つ実体クラスを代入
ISomeInterface<SubClass> sub = new SomeClass<BaseClass>();

//subの実体の型引数はBaseClassだが、より具体的なSubClassを入力されるぶんには何も問題がない
sub.SetValue(new SubClass());
sub.Prop = new SubClass();
```

実体となるクラスは派生元の`BaseClass`なので、入力されるぶんには、別にさらに具体的なクラスを入力されても何も問題がありません。

####書き込み専用じゃないと…

```csharp
ISomeInterface<SubClass> sub = new SomeClass<BaseClass>();
//書き込み専用じゃなくて読み取りができると…
//subの実体はBaseClassなので、SubClassが出てくるという補償はないのに、SubClassで受けられてしまう！
SubClass subCls = sub.Prop;
```

実体となるクラスは派生元の`BaseClass`であるにも関わらず、`SubClass`として出てきてしまいます。
このように、読み取りには問題が生じることがわかります。

##さいごに
ジェネリックの共変性と反変性はややこしくてこんがらがるので、簡単にまとめました。
なにかご指摘等ありましたらコメント頂けると嬉しいです！