Partilhar via


Anotações para parâmetros de tipo sem restrições

Observação

Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui mudanças de especificação propostas, juntamente com as informações necessárias durante o design e desenvolvimento do recurso. Estes artigos são publicados até que as alterações de especificações 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 Language Design Meeting (LDM).

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

Questão campeã: https://github.com/dotnet/csharplang/issues/3297

Resumo

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

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

? anotação

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

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

Se um parâmetro de tipo T for substituído por um tipo de referência, T? representa 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? representa 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?, então T? representa 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, então T? representa U?, mesmo dentro de um contexto #nullable disable.

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

Para valores de retorno, 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>()
}

default restrição

Para compatibilidade com o código existente, onde métodos genéricos substituídos e explicitamente implementados não podem incluir cláusulas de restrição explícitas, T? em um método substituído ou explicitamente implementado é tratado como Nullable<T> onde 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 que foi substituído ou implementado explicitamente.

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 estã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 de default que não seja numa 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