Compartilhar via


Anotações de parâmetro de tipo sem restrição

Nota

Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui alterações de especificação propostas, juntamente com as informações necessárias durante o design e o desenvolvimento do recurso. Esses artigos são publicados até que as alterações de especificação propostas sejam finalizadas e incorporadas na especificação ECMA atual.

Pode haver algumas discrepâncias entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da reunião de design de idioma (LDM).

Você pode saber mais sobre o processo de adoção de speclets de recursos no padrão de linguagem C# no artigo sobre as especificações de .

Problema do especialista: https://github.com/dotnet/csharplang/issues/3297

Resumo

Permitir anotações anuláveis para parâmetros de tipo que não são restritos a tipos de valor ou tipos de referência: T?.

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

Anotação de ?

No C#8, ? anotações só podiam ser aplicadas a parâmetros de tipo que eram explicitamente restritos a tipos de valor ou tipos de referência. No C#9, as anotações ? podem ser aplicadas a qualquer parâmetro de tipo, independentemente das restrições.

A menos que um parâmetro de tipo seja explicitamente restrito a tipos de valor, as anotações só podem ser aplicadas em um contexto #nullable enable.

Se um parâmetro de tipo T for substituído por um tipo de referência, T? representará uma instância anulável desse tipo de referência.

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

Se T for substituído por um tipo de valor, T? representará uma instância de T.

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

Se T for substituído por um tipo anotado U?, T? representará o tipo anotado U? em vez de U??.

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

Se T for substituído por um tipo U, T? representará U?, mesmo dentro de um contexto de #nullable disable.

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

Para valores retornados, T? é equivalente a [MaybeNull]T; para valores de argumento, T? é equivalente a [AllowNull]T. A equivalência é importante ao substituir ou implementar interfaces de um assembly compilado com 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>()
}

Restrição default

Para compatibilidade com o código existente em que métodos genéricos substituídos e explicitamente implementados não podiam incluir cláusulas de restrição explícitas, T? em um método substituído ou explicitamente implementado é tratado como Nullable<T> em que T é um tipo de valor.

Para permitir anotações para parâmetros de tipo restritos a tipos de referência, o C#8 permitiu restrições explícitas de where T : class e where T : struct no método substituído ou explicitamente implementado.

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

Para permitir anotações para parâmetros de tipo que não são restritos a tipos de referência ou tipos de valor, o C#9 permite uma nova restrição de 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 { }
}

É um erro usar uma restrição default fora de uma substituição de método ou implementação explícita. É um erro usar uma restrição de default quando o parâmetro de tipo correspondente no método substituído ou de interface é restrito a um tipo de referência ou tipo de valor.

Reuniões de design