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
C# feature specifications