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