where — Ograniczenie typu ogólnego (odwołanie w C#)
Klauzula where
w definicji ogólnej określa ograniczenia typów, które są używane jako argumenty dla parametrów typu w typie ogólnym, metodzie, delegatu lub funkcji lokalnej. Ograniczenia mogą określać interfejsy, klasy bazowe lub wymagać typu ogólnego jako odwołania, wartości lub niezarządzanego typu. Deklarują możliwości, które muszą mieć argument typu i muszą zostać umieszczone po zadeklarowanej klasie bazowej lub zaimplementowanych interfejsach.
Na przykład można zadeklarować klasę ogólną , AGenericClass
tak aby parametr T
type implementuje IComparable<T> interfejs:
public class AGenericClass<T> where T : IComparable<T> { }
Uwaga
Aby uzyskać więcej informacji na temat klauzuli where w wyrażeniu zapytania, zobacz klauzulę where.
Klauzula where
może również zawierać ograniczenie klasy bazowej. Ograniczenie klasy bazowej stwierdza, że typ, który ma być używany jako argument typu dla tego typu ogólnego, ma określoną klasę jako klasę bazową lub jest klasą bazową. Jeśli jest używane ograniczenie klasy bazowej, musi pojawić się przed wszelkimi innymi ograniczeniami dla tego parametru typu. Niektóre typy są niedozwolone jako ograniczenie klasy bazowej: Object, Arrayi ValueType. W poniższym przykładzie przedstawiono typy, które można teraz określić jako klasę bazową:
public class UsingEnum<T> where T : System.Enum { }
public class UsingDelegate<T> where T : System.Delegate { }
public class Multicaster<T> where T : System.MulticastDelegate { }
W kontekście dopuszczania wartości null wymuszana jest wartość null typu klasy bazowej. Jeśli klasa bazowa jest niepusta (na przykład Base
), argument typu musi być niepusty. Jeśli klasa bazowa jest dopuszczana do wartości null (na przykład Base?
), argument typu może być typem odwołania dopuszczalnym do wartości null lub innym niż null. Kompilator wystawia ostrzeżenie, jeśli argument typu jest typem odwołania dopuszczanym do wartości null, gdy klasa bazowa jest niepusta.
Klauzula where
może określać, że typ to class
lub struct
. Ograniczenie struct
eliminuje konieczność określenia ograniczenia klasy bazowej klasy System.ValueType
. Nie System.ValueType
można użyć typu jako ograniczenia klasy bazowej. W poniższym przykładzie przedstawiono ograniczenia class
i :struct
class MyClass<T, U>
where T : class
where U : struct
{ }
W kontekście class
dopuszczania wartości null ograniczenie wymaga, aby typ był typem referencyjnym, który nie może mieć wartości null. Aby zezwolić na typy odwołań dopuszczających wartości null, użyj class?
ograniczenia, które zezwala zarówno na typy referencyjne dopuszczane do wartości null, jak i bez wartości null.
Klauzula where
notnull
może zawierać ograniczenie. Ograniczenie notnull
ogranicza parametr typu do typów innych niż null. Typ może być typem wartości lub typem odwołania bez wartości null. Ograniczenie notnull
jest dostępne dla kodu skompilowanego w nullable enable
kontekście. W przeciwieństwie do innych ograniczeń, jeśli argument typu narusza notnull
ograniczenie, kompilator generuje ostrzeżenie zamiast błędu. Ostrzeżenia są generowane tylko w nullable enable
kontekście.
Dodanie typów odwołań dopuszczanych do wartości null wprowadza potencjalną niejednoznaczność w znaczeniu T?
metod ogólnych. Jeśli T
element ma wartość struct
, T?
jest taki sam jak System.Nullable<T>. Jeśli jednak T
jest typem odwołania, oznacza to, T?
że null
jest to prawidłowa wartość. Niejednoznaczność pojawia się, ponieważ zastępowanie metod nie może obejmować ograniczeń. Nowe default
ograniczenie rozwiązuje tę niejednoznaczność. Dodajesz ją, gdy klasa bazowa lub interfejs deklaruje dwa przeciążenia metody, jeden, który określa struct
ograniczenie, i taki, który nie ma zastosowanego struct
ograniczenia lub class
:
public abstract class B
{
public void M<T>(T? item) where T : struct { }
public abstract void M<T>(T? item);
}
Ograniczenie służy default
do określania, że klasa pochodna zastępuje metodę bez ograniczeń w klasie pochodnej lub jawnej implementacji interfejsu. Jest ona prawidłowa tylko w przypadku metod, które zastępują metody podstawowe lub jawne implementacje interfejsu:
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 { }
}
Ważne
Deklaracje ogólne, które obejmują notnull
ograniczenie, mogą być używane w kontekście bezpłciowym, ale kompilator nie wymusza ograniczenia.
#nullable enable
class NotNullContainer<T>
where T : notnull
{
}
#nullable restore
Klauzula where
może również zawierać unmanaged
ograniczenie. Ograniczenie unmanaged
ogranicza parametr typu do typów znanych jako typy niezarządzane. Ograniczenie unmanaged
ułatwia pisanie kodu międzyoperacyjnego niskiego poziomu w języku C#. To ograniczenie umożliwia wykonywanie procedur wielokrotnego użytku we wszystkich niezarządzanych typach.
unmanaged
Ograniczenie nie może być łączone z ograniczeniem class
lub struct
. Ograniczenie unmanaged
wymusza, że typ musi mieć wartość struct
:
class UnManagedWrapper<T>
where T : unmanaged
{ }
Klauzula where
może również zawierać ograniczenie konstruktora. new()
To ograniczenie umożliwia utworzenie wystąpienia parametru typu przy użyciu new
operatora . Ograniczenie new() pozwala kompilatorowi wiedzieć, że każdy podany argument typu musi mieć dostępny konstruktor bez parametrów. Na przykład:
public class MyGenericClass<T> where T : IComparable<T>, new()
{
// The following line is not possible without new() constraint:
T item = new T();
}
Ograniczenie new()
jest wyświetlane jako ostatnie w klauzuli where
, chyba że następuje allows ref struct
po nim ograniczenie anty-ograniczenie. Ograniczenie new()
nie może być łączone z struct
ograniczeniami lub unmanaged
. Wszystkie typy spełniające te ograniczenia muszą mieć dostępny konstruktor bez parametrów, co sprawia, że new()
ograniczenie jest nadmiarowe.
To ograniczenie anty-ograniczenie deklaruje, że argument T
typu może być typem ref struct
. Na przykład:
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)
{
}
}
Typ ogólny lub metoda musi przestrzegać reguł bezpieczeństwa ref dla dowolnego wystąpienia, T
ponieważ może to być ref struct
. Nie allows ref struct
można połączyć klauzuli z ograniczeniem class
lub class?
. Ograniczenie allows ref struct
anty-ograniczenie musi być zgodne ze wszystkimi ograniczeniami dla tego typu argumentu.
W przypadku parametrów wielu typów należy użyć jednej where
klauzuli dla każdego parametru typu, na przykład:
public interface IMyInterface { }
namespace CodeExample
{
class Dictionary<TKey, TVal>
where TKey : IComparable<TKey>
where TVal : IMyInterface
{
public void Add(TKey key, TVal val) { }
}
}
Ograniczenia można również dołączyć do parametrów typu metod ogólnych, jak pokazano w poniższym przykładzie:
public void MyMethod<T>(T t) where T : IMyInterface { }
Zwróć uwagę, że składnia opisujący ograniczenia parametrów typu dla delegatów jest taka sama jak w przypadku metod:
delegate T MyDelegate<T>() where T : new();
Aby uzyskać informacje na temat delegatów ogólnych, zobacz Delegaty ogólne.
Aby uzyskać szczegółowe informacje na temat składni i używania ograniczeń, zobacz Ograniczenia dotyczące parametrów typu.
specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz Specyfikacja języka C#. Specyfikacja języka jest ostatecznym źródłem informacji o składni i użyciu języka C#.