Udostępnij za pośrednictwem


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ą , AGenericClasstak 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 wherenotnull 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#.

Zobacz też