C# 共変性と反変性(in / out)

共変性(Covariance)と反変性(Contravariance)は、ジェネリック型の型パラメータに対する変換の規則を定めるものです。inout キーワードを使って指定します。

問題の背景

DogAnimal を継承していても、List<Dog>List<Animal> として扱えません。

class Animal { }
class Dog : Animal { }

List&lt;Dog&gt; dogs = new List&lt;Dog&gt;();
// List&lt;Animal&gt; animals = dogs; // コンパイルエラー!

これは型安全性を守るためです。もし許可されると、animals.Add(new Cat()) のような操作で List<Dog> に猫が入ってしまいます。

共変性(out キーワード)

共変性は「出力のみに使う型パラメータ」に適用します。out キーワードで指定します。

public interface IProducer&lt;out T&gt;
{
    T Produce(); // T を返す(出力)のみ
}

共変の場合、IProducer<Dog>IProducer<Animal> として扱えます。

class AnimalProducer : IProducer&lt;Animal&gt;
{
    public Animal Produce() =&gt; new Animal();
}

class DogProducer : IProducer&lt;Dog&gt;
{
    public Dog Produce() =&gt; new Dog();
}

IProducer&lt;Animal&gt; producer = new DogProducer(); // OK
Animal animal = producer.Produce(); // Dog が返る

反変性(in キーワード)

反変性は「入力のみに使う型パラメータ」に適用します。in キーワードで指定します。

public interface IConsumer&lt;in T&gt;
{
    void Consume(T item); // T を受け取る(入力)のみ
}

反変の場合、IConsumer<Animal>IConsumer<Dog> として扱えます(方向が逆)。

class AnimalConsumer : IConsumer&lt;Animal&gt;
{
    public void Consume(Animal item) =&gt; Console.WriteLine("Animal を消費");
}

IConsumer&lt;Dog&gt; dogConsumer = new AnimalConsumer(); // OK
dogConsumer.Consume(new Dog()); // Dog は Animal でもあるので OK

共変性と反変性の比較

共変性(out)

より派生した型を、基底型として扱える。出力専用。

反変性(in)

より基底の型を、派生型として扱える。入力専用。

.NET の実例

.NET Framework には共変・反変を活用したインターフェースがあります。

IEnumerable<out T>

共変。IEnumerable&lt;Dog&gt;IEnumerable&lt;Animal&gt; として扱える。

IComparer<in T>

反変。IComparer&lt;Animal&gt;IComparer&lt;Dog&gt; として扱える。

Func<out TResult>

戻り値の型は共変。

Action<in T>

引数の型は反変。

IEnumerable&lt;Dog&gt; dogs = new List&lt;Dog&gt; { new Dog() };
IEnumerable&lt;Animal&gt; animals = dogs; // 共変により OK

Action&lt;Animal&gt; animalAction = a =&gt; Console.WriteLine("Animal");
Action&lt;Dog&gt; dogAction = animalAction; // 反変により OK

制限事項

共変・反変は、インターフェースとデリゲートにのみ適用できます。クラスには適用できません。また、out を付けた型パラメータは戻り値にのみ、in を付けた型パラメータは引数にのみ使用できます。

共変性と反変性を理解することで、より柔軟で安全な API 設計が可能になります。