deezus blog

.Net Core、Typescriptを中心に技術的ノウハウを公開しています

ジェネリック型制約

はじめに

C#では関数に型引数を指定することができます

public void TestFunction<T>(){
  // 何かしらの処理
}

ジェネリック制約

型引数は便利なのですが、どんな型でも許容するため状況によっては使いにくい場合もあります
指定可能な型を制限したい場合、関数の後ろにwhereと記載し、様々な制約を指定することができます
これをジェネリック制約といいます

new()制約

例えば下記のようにすると、インスタンス作成可能な型のみ指定可能になり、静的クラスなどは指定できなくなります

class Program
{
    public void TestFunction<T>() where new(){
        // 何かしらの処理
    }

    public class TestClass1{
    }

    public static class TestClass2{
    }

    static void Main(string[] args)
    {
        TestFunction<TestClass1>(); // ←OK
        TestFunction<TestClass2>(); // ←静的クラスのなのでコンパイルエラー
    }
}

new()の制約をつけるメリットはTのインスタンスを簡単に作れることです

public void TestFunction<T>() where new(){
    var result = new T(); // ←Tはインスタンス作成可能が保証されているためこのように書ける
}

なお、制約をつけない場合は同様の処理は下記のようになります
new()制約をつけない場合よりもインスタンス作成に時間がかかるので、関数内でインスタンス作成を行う場合はつけたほうがいいでしょう

public void TestFunction<T>() where new(){
    var result = (T) Activator.CreateInstance(typeof(T));
}

インターフェース制約

指定したinterfaceを実装した型のみに制限することができます

class Program
{
    public void TestFunction<T>() where ITest, new(){
        var data = new T();
        data.Execute();    // ←ITest実装を保証されているのでExecute()が呼び出せる
    }

    public class TestClass1 : ITest
    {
        public void Execute()
        {
            
        }
    }

    public class TestClass2
    {
        
    }

    public interface ITest{
        void Execute();
    }

    static void Main(string[] args)
    {
        TestFunction<TestClass1>(); // ←OK
        TestFunction<TestClass2>(); // ←ITestを実装していないのでコンパイルエラー
    }
}

上記のように複数制約を指定する場合、new()制約は最後に記載する必要があります