Condividi tramite


Annotazioni dei parametri di tipo non vincolato

Nota

Questo articolo è una specifica di funzionalità. La specifica funge da documento di progettazione per la funzionalità. Include le modifiche specifiche proposte, insieme alle informazioni necessarie durante la progettazione e lo sviluppo della funzionalità. Questi articoli vengono pubblicati fino a quando le modifiche specifiche proposte non vengono completate e incorporate nella specifica ECMA corrente.

Potrebbero verificarsi alcune discrepanze tra la specifica di funzionalità e l'implementazione completata. Tali differenze vengono acquisite nelle note pertinenti del language design meeting (LDM) .

Altre informazioni sul processo per l'adozione di speclet di funzionalità nello standard del linguaggio C# sono disponibili nell'articolo sulle specifiche di .

Problema del campione: https://github.com/dotnet/csharplang/issues/3297

Sommario

Consenti annotazioni di nullabilità per i parametri di tipo che non sono vincolati ai tipi di valore o ai tipi di riferimento: T?.

static T? FirstOrDefault<T>(this IEnumerable<T> collection) { ... }

? annotazione

In C#8 le annotazioni ? possono essere applicate solo ai parametri di tipo vincolati in modo esplicito ai tipi valore o ai tipi riferimento. In C#9 ? annotazioni possono essere applicate a qualsiasi parametro di tipo, indipendentemente dai vincoli.

A meno che un parametro di tipo non sia vincolato in modo esplicito ai tipi valore, le annotazioni possono essere applicate solo all'interno di un contesto di #nullable enable.

Se un parametro di tipo T viene sostituito con un tipo riferimento, T? rappresenta un'istanza nullable di tale tipo di riferimento.

var s1 = new string[0].FirstOrDefault();  // string? s1
var s2 = new string?[0].FirstOrDefault(); // string? s2

Se T viene sostituito con un tipo valore, T? rappresenta un'istanza di T.

var i1 = new int[0].FirstOrDefault();  // int i1
var i2 = new int?[0].FirstOrDefault(); // int? i2

Se T viene sostituito con un tipo con annotazioni U?, T? rappresenta il tipo annotato U? anziché U??.

var u1 = new U[0].FirstOrDefault();  // U? u1
var u2 = new U?[0].FirstOrDefault(); // U? u2

Se T viene sostituito con un tipo U, T? rappresenta U?, anche all'interno di un contesto di #nullable disable.

#nullable disable
var u3 = new U[0].FirstOrDefault();  // U? u3

Per i valori restituiti, T? equivale a [MaybeNull]T; per i valori degli argomenti, T? equivale a [AllowNull]T. L'equivalenza è importante quando si esegue l'override o l'implementazione di interfacce da un assembly compilato con C#8.

public abstract class A
{
    [return: MaybeNull] public abstract T F1<T>();
    public abstract void F2<T>([AllowNull] T t);
}

public class B : A
{
    public override T? F1<T>() where T : default { ... }       // matches A.F1<T>()
    public override void F2<T>(T? t) where T : default { ... } // matches A.F2<T>()
}

vincolo default

Per compatibilità con il codice esistente in cui i metodi generici sottoposti a override e implementati in modo esplicito non possono includere clausole di vincolo esplicite, T? in un metodo sottoposto a override o implementato in modo esplicito viene considerato come Nullable<T> in cui T è un tipo valore.

Per consentire annotazioni per i parametri di tipo vincolati ai tipi di riferimento, C#8 ha permesso esplicitamente i vincoli where T : class e where T : struct sul metodo sovrascritto o implementato in modo esplicito.

class A1
{
    public virtual void F1<T>(T? t) where T : struct { }
    public virtual void F1<T>(T? t) where T : class { }
}

class B1 : A1
{
    public override void F1<T>(T? t) /*where T : struct*/ { }
    public override void F1<T>(T? t) where T : class { }
}

Per consentire annotazioni per i parametri di tipo non vincolati ai tipi di riferimento o ai tipi valore, C#9 consente un nuovo vincolo where T : default.

class A2
{
    public virtual void F2<T>(T? t) where T : struct { }
    public virtual void F2<T>(T? t) { }
}

class B2 : A2
{
    public override void F2<T>(T? t) /*where T : struct*/ { }
    public override void F2<T>(T? t) where T : default { }
}

È un errore usare un vincolo di default diverso da un override del metodo o un'implementazione esplicita. È un errore usare un vincolo default quando il parametro di tipo corrispondente nel metodo di interfaccia o sottoposto a override è vincolato a un tipo riferimento o a un tipo valore.

Riunioni di progettazione