where (vincolo di tipo generico) (Riferimenti per C#)

La clausola where in una definizione generica specifica i vincoli per i tipi che vengono usati come argomenti per i parametri di tipo in un tipo generico, metodo, delegato o funzione locale. I vincoli possono specificare interfacce, classi di base o richiedere che un tipo generico sia un riferimento, un valore o un tipo non gestito. Dichiarano funzionalità che l'argomento del tipo deve avere e devono essere inserite dopo qualsiasi classe di base dichiarata o interfacce implementate.

Ad esempio, una classe generica, AGenericClass, può essere dichiarata in modo che tramite il parametro di tipo T venga implementata l'interfaccia IComparable<T>:

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

Nota

Per altre informazioni sulla clausola where in un'espressione di query, vedere Clausola where.

La clausola where può inoltre includere un vincolo di classe di base. Il vincolo della classe base indica che un tipo da usare come argomento di tipo per tale tipo generico ha la classe specificata come classe base o è quella classe base. Se viene usato il vincolo della classe di base, deve apparire prima di tutti gli altri vincoli per quel parametro di tipo. Alcuni tipi non sono consentiti come vincoli di classe di base: Object, Array e ValueType. L'esempio seguente illustra i tipi possono ora essere specificati come una classe di base:

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

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

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

In un contesto nullable, viene applicata l'impostazione del valore Nullability del tipo di classe di base. Se la classe base non è nullable (ad esempio Base), l'argomento di tipo deve essere non nullable. Se la classe base è nullable (ad esempio Base?), l'argomento di tipo può essere un tipo riferimento nullable o non nullable. Il compilatore genera un avviso se l'argomento di tipo è un tipo riferimento nullable quando la classe base non è nullable.

La clausola where può specificare che il tipo è un oggetto class o struct. Il vincolo struct elimina la necessità di specificare un vincolo di classe di base di System.ValueType. Il tipo System.ValueType non può essere usato come vincolo di classe di base. Nell'esempio seguente vengono illustrati i vincoli class e struct:

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

In un contesto nullable, il vincolo class richiede che un tipo sia un tipo di riferimento non nullable. Per consentire i tipi riferimento nullable, usare il vincolo class?, che consente tipi riferimento nullable e non nullable.

La clausola where può includere il vincolo notnull. Il vincolo notnull limita il parametro di tipo a tipi non nullable. Il tipo può essere un tipo valore o un tipo riferimento non nullable. Il vincolo notnull è disponibile per il codice compilato in un contesto nullable enable. A differenza di altri vincoli, se un argomento di tipo viola il vincolo notnull, il compilatore genera un avviso anziché un errore. Gli avvisi vengono generati solo in un contesto di nullable enable.

L'aggiunta di tipi riferimento nullable introduce una potenziale ambiguità nel significato di T? nei metodi generici. Se T è struct, T? è uguale a System.Nullable<T>. Tuttavia, se T è un tipo riferimento, T? significa che null è un valore valido. L'ambiguità si verifica perché l'override dei metodi non può includere vincoli. Il nuovo vincolo default risolve questa ambiguità. Verrà aggiunto quando una classe o un'interfaccia di base dichiara due overload di un metodo, uno che specifica il vincolo struct e uno che non ha il vincolo struct o class applicato:

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

}

Usare il vincolo default per specificare che la classe derivata esegue l'override del metodo senza il vincolo nella classe derivata o l'implementazione esplicita dell'interfaccia. È valido solo nei metodi che eseguono l'override dei metodi di base o implementazioni esplicite dell'interfaccia:

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

Importante

Le dichiarazioni generiche che includono il vincolo notnull possono essere usate in un contesto nullable oblivious, ma il compilatore non applica il vincolo.

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

La clausola where può anche includere un vincolo unmanaged. Il vincolo unmanaged limita il parametro di tipo ai tipi noti come tipi non gestiti. Il vincolo unmanaged rende più semplice la scrittura di codice di interoperabilità di basso livello in C#. Questo vincolo abilita le routine riutilizzabili in tutti i tipi non gestiti. Il vincolo unmanaged non può essere combinato con il vincolo class o struct. Il vincolo unmanaged impone che il tipo deve essere un elemento struct:

class UnManagedWrapper<T>
    where T : unmanaged
{ }

La clausola where può anche includere un vincolo di costruttore, new(). Tale vincolo consente di creare un'istanza di un parametro di tipo usando l'operatore new. Il vincolo new() consente al compilatore di sapere che qualsiasi argomento di tipo fornito deve avere un costruttore senza parametri accessibile. Ad esempio:

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

Il vincolo new() viene visualizzato per ultimo nella clausola where. Il vincolo new() non può essere combinato con i vincoli struct o unmanaged. Tutti i tipi che soddisfano i vincoli devono avere un costruttore senza parametri accessibile, per rendere il vincolo new() ridondante.

Con più parametri di tipo, usare una clausola where per ogni parametro di tipo, ad esempio:

public interface IMyInterface { }

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

È anche possibile associare vincoli ai parametri di tipo di metodi generici, come illustrato nell'esempio seguente:

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

Si noti che la sintassi usata per descrivere i vincoli dei parametri di tipo per i delegati è uguale a quella dei metodi:

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

Per informazioni sui delegati generici, vedere Delegati generici.

Per informazioni dettagliate sulla sintassi e sull'uso dei vincoli, vedere Vincoli sui parametri di tipo.

Specifiche del linguaggio C#

Per altre informazioni, vedere la specifica del linguaggio C#. La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#.

Vedi anche