【C#】リストのジェネリックを親クラスに変換する

List<T> の T を親クラスやインターフェースに変換したいこと無いですか?継承関係があって安全に変換できるならジェネリックの型は親クラスに互換してても良さそうですが List の T では認められていません。この操作はできないので代替案の話になります。

たとえば以下のような定義はだと型が違うエラーになります。

// <T> がこんな感じに宣言されている

// インターフェースの宣言
public interface ISample
{
    int A { get; set; }
}

// 実際の実装クラス
public class Sample : ISample
{
    public int A { get; set; }
    public int B { get; set; }
}

以下のように親クラスなジェネリックの引数には指定できません。

public static void Main(params string[] args)
{
    // 実装クラスでリストのジェネリックを宣言する
    var list = new List<Sample>();

    // CS1503 引数 1: は
    //  'System.Collections.Generic.List<ConsoleApp26.Derived>' から
    //  'System.Collections.Generic.List<ConsoleApp26.Base>' へ変換することはできません
    Foo(list);

    // これは呼び出せる
    Bar(list);
}

// エラー
public static void Foo(List<ISample> list)
{
    // any
}

// OK
public static void Bar(IEnumerable<ISample> list)
{
    // any
}

こういう時は、IEnumerable で渡すことができます。

また、どうしても渡したい場合以下のように Cast → ToList すれば渡せます、ただしこれ、新しく別のリストを作成し渡しているため、渡した先でリストにAdd/Removeしても呼び出し元のリストは変化しません(恐らく「そうじゃないんだよな」というケースが多いと思いますが…)

// 無理やり同じ型に変換する
Foo(list.Cast<ISample>().ToList());

なので、そういった用途が想定される場合、毎回中身をキャストする、もしくは、最初からインターフェースや親クラスでリストを宣言する、が最終的な答えかと思います。

// 左辺で受けるときにこうやってキャストできるためこれを利用する
foreach(ISample s in list)
{
   // ...

// 最初から T をインターフェースや親のクラスで宣言する
var list = new List<ISample>();

以上です。