where (ジェネリック型制約) (C# リファレンス)

ジェネリック定義の where 句では、型の制約を指定します。この型は、ジェネリック型、メソッド、デリゲート、またはローカル関数における型パラメーターの引数として使用されます。 制約では、インターフェイス (基底クラス) を指定したり、参照、値、またはアンマネージド型となるジェネリック型を要求したりすることができます。 型引数に必要な機能を宣言し、宣言された基底クラスまたは実装されたインターフェイスの後に配置する必要があります。

たとえば、型パラメーター TIComparable<T> インターフェイスを実装するように、次のように AGenericClass ジェネリック クラスを宣言できます。

public class AGenericClass<T> where T : IComparable<T> { }

注意

クエリ式での where 句の詳細については、「where 句」を参照してください。

where 句には基底クラスの制約を含めることもできます。 基底クラスの制約は、そのジェネリック型の型引数として使用される型が、指定されたクラスを基底クラスとして持つか、その基底クラスであることを示しています。 基底クラスの制約を使用する場合は、型パラメーターに関する制約よりも前に制約を記述する必要があります。 一部の型は、基底クラスの制約として許可されません (ObjectArrayValueType)。 次の例では、この型は基底クラスとして指定できるようになったことを示しています。

public class UsingEnum<T> where T : System.Enum { }

public class UsingDelegate<T> where T : System.Delegate { }

public class Multicaster<T> where T : System.MulticastDelegate { }

null 許容コンテキストでは、基本クラス型の null 許容属性が適用されます。 基底クラスが null 非許容の場合 (たとえば、Base)、型引数は null 非許容である必要があります。 基底クラスが null 許容の場合 (Base? など)、型引数は null 許容型または null 非許容型のいずれかになります。 基底クラスが null 非許容であるときに、型引数が null 許容の参照型である場合、コンパイラからは警告を発行されます。

where 句では、型が class または struct であることを指定できます。 struct 制約では、System.ValueType の基底クラスの制約を指定する必要はありません。 System.ValueType 型は基底クラスの制約として使用できません。 class 制約と struct 制約の両方の例を次に示します。

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

null 許容コンテキストでは、class 制約には、型が null 非許容の参照型である必要があります。 null 許容の参照型を許可するには、class? 制約を使用して、null 許容と null 非許容の参照型の両方を許可します。

where 句には、notnull 制約を含めることができます。 notnull 制約では、型パラメーターを null 非許容型に制限します。 型には、値の型または null 非許容参照型を指定できます。 notnull 制約は、nullable enable コンテキストでコンパイルされたコードで使用できます。 他の制約とは異なり、型引数が notnull 制約に違反すると、コンパイラによりエラーではなく警告が生成されます。 警告は、nullable enable コンテキストでのみ生成されます。

null 許容参照型を追加すると、ジェネリック メソッドの T? の意味に潜在的な曖昧さが生じます。 Tstruct である場合、T?System.Nullable<T> と同じです。 しかし、T が参照型の場合は、T?null が有効な値であることを意味します。 このような曖昧さは、オーバーライド メソッドに制約を含めることができないために生じます。 新しい default 制約によって、この曖昧さが解決されます。 基底クラスまたはインターフェイスでメソッドの 2 つのオーバーロードが宣言されているときに (struct 制約が指定されているものと、struct または class 制約が適用されていないもの)、それを追加します。

public abstract class B
{
    public void M<T>(T? item) where T : struct { }
    public abstract void M<T>(T? item);

}

派生クラスで制約を使用せずにメソッドをオーバーライドすること、または明示的なインターフェイスの実装を指定するには、default 制約を使用します。 これは、基本メソッドをオーバーライドするメソッド、または明示的なインターフェイス実装でのみ有効です。

public class D : B
{
    // Without the "default" constraint, the compiler tries to override the first method in B
    public override void M<T>(T? item) where T : default { }
}

重要

notnull 制約が含まれるジェネリック宣言は、null 許容が未指定のコンテキストで使用できますが、コンパイラではその制約は強制されません。

#nullable enable
    class NotNullContainer<T>
        where T : notnull
    {
    }
#nullable restore

where 句には、unmanaged 制約を含めることもできます。 unmanaged 制約では、アンマネージド型と呼ばれる型に対して型パラメーターを制限します。 unmanaged 制約を使用すると、C# でローレベルの相互運用コードを記述しやすくなります。 この制約では、すべてのアンマネージド型にわたって再利用可能なルーチンを可能にします。 unmanaged 制約は、classstruct 制約と組み合わせることはできません。 unmanaged 制約は struct にする必要がある型を適用します。

class UnManagedWrapper<T>
    where T : unmanaged
{ }

where 句には、コンストラクター制約 new() を含めることもできます。 その制約では、new 演算子を使用して型パラメーターのインスタンスを作成できるようにします。 new() 制約に基づいて、コンパイラで、指定されている型引数にはアクセス可能なパラメーターなしのコンストラクターが必要であることが認識されます。 次に例を示します。

public class MyGenericClass<T> where T : IComparable<T>, new()
{
    // The following line is not possible without new() constraint:
    T item = new T();
}

new() 制約は where 句の最後に示されます。 new() 制約は、structunmanaged 制約と組み合わせることはできません。 それらの制約を満たすすべての型には、new() 制約を重複させて、アクセス可能なパラメーターなしのコンストラクターが含まれている必要があります。

複数の型パラメーターがある場合には、型パラメーターごとに where 句を 1 つずつ使用します。次に例を示します。

public interface IMyInterface { }

namespace CodeExample
{
    class Dictionary<TKey, TVal>
        where TKey : IComparable<TKey>
        where TVal : IMyInterface
    {
        public void Add(TKey key, TVal val) { }
    }
}

次の例に示すように、ジェネリック メソッドの型パラメーターにも制約を適用できます。

public void MyMethod<T>(T t) where T : IMyInterface { }

デリゲートに対する型パラメーター制約を記述する構文は、メソッドの構文と同じである点に注意してください。

delegate T MyDelegate<T>() where T : new();

汎用デリゲートについては、「汎用デリゲート」を参照してください。

制約の構文と使用方法の詳細については、「型パラメーターの制約」を参照してください。

C# 言語仕様

詳細については、「C# 言語の仕様」を参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。

関連項目