where (ジェネリック型制約) (C# リファレンス)
ジェネリック定義の where
句では、型の制約を指定します。この型は、ジェネリック型、メソッド、デリゲート、またはローカル関数における型パラメーターの引数として使用されます。 制約では、インターフェイス (基底クラス) を指定したり、参照、値、またはアンマネージド型となるジェネリック型を要求したりすることができます。 型引数に必要な機能を宣言し、宣言された基底クラスまたは実装されたインターフェイスの後に配置する必要があります。
たとえば、型パラメーター T
が IComparable<T> インターフェイスを実装するように、次のように AGenericClass
ジェネリック クラスを宣言できます。
public class AGenericClass<T> where T : IComparable<T> { }
注意
クエリ式での where 句の詳細については、「where 句」を参照してください。
where
句には基底クラスの制約を含めることもできます。 基底クラスの制約は、そのジェネリック型の型引数として使用される型が、指定されたクラスを基底クラスとして持つか、その基底クラスであることを示しています。 基底クラスの制約を使用する場合は、型パラメーターに関する制約よりも前に制約を記述する必要があります。 一部の型は、基底クラスの制約として許可されません (Object、Array、ValueType)。 次の例では、この型は基底クラスとして指定できるようになったことを示しています。
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?
の意味に潜在的な曖昧さが生じます。 T
が struct
である場合、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
制約は、class
や struct
制約と組み合わせることはできません。 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
句の最後にあります (その後に反制約 allows ref struct
が続いている場合を除きます)。 new()
制約は、struct
や unmanaged
制約と組み合わせることはできません。 それらの制約を満たすすべての型には、new()
制約を重複させて、アクセス可能なパラメーターなしのコンストラクターが含まれている必要があります。
この反制約では、T
の型引数を ref struct
型にできることを宣言します。 次に例を示します。
public class GenericRefStruct<T> where T : allows ref struct
{
// Scoped is allowed because T might be a ref struct
public void M(scoped T parm)
{
}
}
ジェネリック型またはメソッドは、ref struct
である可能性があるため、T
のインスタンスに関する安全性参照規則に従う必要があります。 allows ref struct
句は、class
制約または class?
制約と組み合わせることはできません。 allows ref struct
の反制約は、その型引数のすべての制約に従う必要があります。
複数の型パラメーターがある場合には、型パラメーターごとに 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# の構文と使用法に関する信頼性のある情報源です。
関連項目
.NET