where (allmän typbegränsning) (C#-referens)

where Satsen i en allmän definition anger begränsningar för de typer som används som argument för typparametrar i en allmän typ, metod, ombud eller lokal funktion. Begränsningar kan ange gränssnitt, basklasser eller kräva att en allmän typ är en referens, ett värde eller en ohanterad typ. De deklarerar funktioner som typargumentet måste ha och måste placeras efter alla deklarerade basklasser eller implementerade gränssnitt.

Du kan till exempel deklarera en allmän klass, AGenericClass, så att typparametern T implementerar IComparable<T> gränssnittet:

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

Kommentar

Mer information om where-satsen i ett frågeuttryck finns i where-satsen.

Satsen where kan också innehålla en basklassbegränsning. Basklassvillkoret anger att en typ som ska användas som ett typargument för den generiska typen har den angivna klassen som basklass eller är den basklassen. Om basklassvillkoret används måste det visas före andra begränsningar för den typen av parameter. Vissa typer tillåts inte som en basklassbegränsning: Object, Arrayoch ValueType. I följande exempel visas de typer som nu kan anges som en basklass:

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

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

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

I ett null-sammanhang tillämpas nullbarheten för basklasstypen. Om basklassen inte är nullbar (till exempel Base) måste typargumentet inte vara nullbart. Om basklassen är nullbar (till exempel Base?) kan typargumentet antingen vara en nullbar eller icke-nullbar referenstyp. Kompilatorn utfärdar en varning om typargumentet är en nullbar referenstyp när basklassen inte är nullbar.

Satsen where kan ange att typen är en class eller en struct. Villkoret struct tar bort behovet av att ange en basklassbegränsning för System.ValueType. Typen System.ValueType kan inte användas som en basklassbegränsning. I följande exempel visas både begränsningarna class och struct :

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

I ett null-sammanhang kräver villkoret class att en typ är en referenstyp som inte kan nollföras. Om du vill tillåta null-referenstyper använder du villkoret class? , som tillåter både null- och icke-nullbara referenstyper.

Satsen where kan innehålla villkoret notnull . Begränsningen notnull begränsar typparametern till typer som inte kan null-värdet. Typen kan vara en värdetyp eller en referenstyp som inte går att nolla. Begränsningen notnull är tillgänglig för kod som kompilerats i en nullable enable kontext. Till skillnad från andra villkor genererar kompilatorn en varning i stället för ett fel om ett typargument bryter mot villkoret notnull . Varningar genereras endast i en nullable enable kontext.

Tillägget av nullbara referenstyper medför en potentiell tvetydighet i innebörden av T? i generiska metoder. Om T är en struct, T? är samma som System.Nullable<T>. Men om T är en referenstyp T? innebär det att det null är ett giltigt värde. Tvetydigheten uppstår eftersom åsidosättande metoder inte kan innehålla begränsningar. Den nya default begränsningen löser den här tvetydigheten. Du lägger till den när en basklass eller ett basgränssnitt deklarerar två överlagringar av en metod, en som anger villkoret struct och en som inte har någon eller-begränsningen struct class tillämpad:

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

}

Du använder villkoret default för att ange att den härledda klassen åsidosätter metoden utan begränsningen i din härledda klass eller explicit gränssnittsimplementering. Den är endast giltig för metoder som åsidosätter basmetoder eller explicita gränssnittsimplementeringar:

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 { }
}

Viktigt!

Allmänna deklarationer som innehåller villkoret notnull kan användas i en nullbar omedveten kontext, men kompilatorn tillämpar inte villkoret.

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

Satsen where kan också innehålla en unmanaged begränsning. Begränsningen unmanaged begränsar typparametern till typer som kallas ohanterade typer. Villkoret unmanaged gör det enklare att skriva interop-kod på låg nivå i C#. Den här begränsningen möjliggör återanvändbara rutiner för alla ohanterade typer. Villkoret unmanaged kan inte kombineras med villkoret class eller struct . Villkoret unmanaged framtvingar att typen måste vara :struct

class UnManagedWrapper<T>
    where T : unmanaged
{ }

Satsen where kan också innehålla en konstruktorbegränsning, new(). Den begränsningen gör det möjligt att skapa en instans av en typparameter med operatorn new . Med villkoret new() kan kompilatorn veta att alla typer av argument som anges måste ha en tillgänglig parameterlös konstruktor. Till exempel:

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

Villkoret new() visas sist i where -satsen, såvida det inte följs av anti-begränsningen allows ref struct . Villkoret new() kan inte kombineras med begränsningarna struct eller unmanaged . Alla typer som uppfyller dessa begränsningar måste ha en tillgänglig parameterlös konstruktor, vilket gör begränsningen new() redundant.

Det här antivillkoret deklarerar att typargumentet för T kan vara en ref struct typ. Till exempel:

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)
    {

    }
}

Den generiska typen eller metoden måste följa referenssäkerhetsreglerna för alla instanser av T eftersom det kan vara en ref struct. Satsen allows ref struct kan inte kombineras med villkoret class eller class? . Begränsningsbegränsningen allows ref struct måste följa alla begränsningar för argumentet av den typen.

Med flera typparametrar använder du en where sats för varje typparameter, till exempel:

public interface IMyInterface { }

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

Du kan också koppla begränsningar till typparametrar för generiska metoder, som du ser i följande exempel:

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

Observera att syntaxen för att beskriva typparameterbegränsningar för ombud är densamma som för metoder:

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

Information om allmänna ombud finns i Allmänna ombud.

Mer information om syntax och användning av begränsningar finns i Begränsningar för typparametrar.

Språkspecifikation för C#

Mer information finns i C#-språkspecifikationen. Språkspecifikationen är den slutgiltiga källan för C#-syntax och -användning.

Se även