ジェネリック (Generic)

ジェネリックとはアルゴリズムの再利用を可能とする機能で、C++のテンプレートに代わるものです。C++ テンプレートと C# ジェネリックの違い | MSDN

ジェネリックは、おもにコレクションで利用されています。

ジェネリックの利点

  • 型の安全性 (タイプセーフティ)
  • コードの記述の簡易化
  • パフォーマンスの向上
  • ソースコードの保護

構文

ジェネリック クラス

class MyClass <T>
{
}
ジェネリック クラス - C# プログラミング ガイド | Microsoft Learn

ジェネリック メソッド

void Method<T>(T val)
{
}
ジェネリック メソッド - C# プログラミング ガイド | Microsoft Learn

ジェネリック修飾子 (Generic Modifier)

共変性 (covariance) と反変性 (contravariance) は、変性 (variance) と総称されます。

たとえば基本クラスBaseと、その派生クラスDerivedがあるとき、

class Base { }
class Derived : Base { }

共変性では、派生型を基本型に代入できます。

IEnumerable<Derived> d1 = new List<Derived>();
IEnumerable<Base> b1 = d1; // 派生型を基本型に代入できる

// 逆は不可
IEnumerable<Base> b2 = new List<Base>();
IEnumerable<Derived> d2 = b2; // error CS0266

反変性では、基本型を派生型に代入できます。

Action<Base> b1 = (a) => { };
Action<Derived> d1 = b1; // 基本型を派生型に代入できる

// 逆は不可
Action<Derived> d2 = (a) => { };
Action<Base> b2 = d2; // error CS0266

out

型パラメータが共変 (covariant) であることを示し、指定の型よりも 強い 派生の多い (more derived) 型を使用できるようになります。つまり派生型のインスタンスを、基本型の変数に割り当てられます。out キーワード (ジェネリック修飾子) - C# リファレンス | Microsoft Learn

interface Sample<out A> {}

in

型パラメータが反変 (contravariant) であることを示し、指定の型よりも 弱い 派生の少ない (less derived) 型を使用できるようになります。つまり基本型のインスタンスを、派生型の変数に割り当てられます。in (ジェネリック修飾子) - C# リファレンス | Microsoft Learn

interface Sample<in A> {}

型の制約

where

型パラメータに指定できる型を、制約できます。たとえば次のようにクラスが定義されていると、

class A { }
class B : A { }
class X { }

class MyClass1<T> { }             // 制約なし
class MyClass2<T> where T : A { } // Aを継承するクラスを指定するように要求

これを用いる側は指定されたクラスを継承するか、そのインターフェイスを実装する型を指定する必要があります。

new MyClass1<A>();
new MyClass1<B>();
new MyClass1<X>();

new MyClass2<A>();
new MyClass2<B>();
new MyClass2<X>(); // error CS0311: 型 'X' はジェネリック型またはメソッド 'MyClass2<T>' 内で型パラメーター 'T' として使用できません。'X' から 'A' への暗黙的な参照変換がありません。

制約する型として"class"や"struct"を指定できます。また制約が複数ある場合には、それらを続けて記述します。

class MyClass<T, U>
    where T : class
    where U : struct { }

new制約 (new constraint)

ジェネリック型に、パラメータなしのコンストラクタが必要であることを制約できます。

void Method<T>()  where T : IDisposable, new()
{
    T t = new T();
    t.Dispose();
}

これによりパラメータなしのコンストラクタがあることが保証されるため、new T()のように呼び出せるようになります。これを指定しないと「変数型 'T' のインスタンスは、new() 制約を含まないため、作成できません。」としてCS0304になります。他の制約と併用するときには、カンマ区切りで最後に記述します。

型の取得

型パラメータに指定された型は、typeof()で取得できます。c# - How to get the type of T from a member of a generic class or method - Stack Overflow

void Method<T>(T val)
{
    Type type = typeof(T);
}

参考

参考書

Microsoft Learnから検索