Fiquei me perguntando por que isso acontecia, já que ia contra o que eu esperava. Isso tem a ver com como os tipos Nullable Genéricos são interpretados em C#.
public static int? MyFunction(){
return default;
}
public static T MyFunctionGeneric<T>() {
return default;
}
public static T? MyFunctionGenericNullable<T>(){
return default;
}
public static void main(string[] args){
Console.WriteLine(MyFunction());
Console.WriteLine(MyFunctionGeneric<int?>());
Console.WriteLine(MyFunctionGenericNullable<int>());
}
Como esperado, quando defino o tipo de retorno, int?
o valor retornado padrão é null
.
Este também é o caso quando defino um genérico que retorna seu próprio tipo, porque int?
quando é passado, o valor retornado padrão é null
.
No entanto, no terceiro caso, embora o tipo de retorno seja tecnicamente int?
, o valor padrão retornado é 0
. Eu teria que passar in int?
como meu parâmetro de tipo para obter uma null
resposta. Note que isso acontece mesmo quando eu declaro explicitamentereturn default(T?)
Isso é um pouco inesperado. Alguém tem uma explicação para isso?
É tudo muito confuso porque, como dizem os documentos :
lendo os documentos, você verá uma resposta para sua pergunta (em negrito):
Se quisermos um comportamento diferente, precisamos usar restrições genéricas. A documentação não lista todas as restrições úteis que, no seu caso, seriam
where T:struct
Isso seria compilado com
int?
um tipo de retorno se usado comint
asT
e você obteria o
null
mesmo resultado nos dois primeiros casos.Em C#,
T?
é uma abreviação para duas coisas diferentes, dependendo seT
é um tipo de valor ou um tipo de referência.T?
para tipos de valor (por exemplo,int?
,double?
)Quando T é um tipo de valor,
T?
é uma abreviação deNullable<T>
.T?
para tipos de referência (tipos de referência anuláveis)A partir do C# 8.0,
T?
também é usado para tipos de referência anuláveis quandoT
é um tipo de referência (por exemplo,string?
,MyClass?
). Esse recurso permite que você indique que um tipo de referência pode conter nulo (sem realmente ser encaixotado em umNullable<T>
wrapper). Exemplo:Neste contexto,
string?
significa uma string de tipo de referência anulável que pode conter um valor nulo.T?
para tipos de referência é apenas uma anotação de tempo de compilação que fornece avisos e verificações de nulidade. Não afeta o comportamento de tempo de execução nem introduz umNullable<T>
wrapper.E quanto aos genéricos
T
?É tudo uma questão de restrições.
Como não há nenhuma restrição
T
em seu exemplo, o compilador tratadefault
de forma diferente com base no queT
é instanciado com:Se
T
for um tipo de referência,default
seránull
porquedefault
para tipos de referência énull
.Se
T
for um tipo de valor,default
será o valor padrão desse tipo (por exemplo,0
paraint
).Você pode restringir seu
MyFunctionGenericNullable<T>
tipoT
de valor com uma restrição genérica:...e você verá o comportamento que provavelmente esperava originalmente. (Não retorna
0
.)A razão para isso é que marcar uma função como retornando
T?
significa apenas que ela pode retornar nulo. Mas o tipo deT
nesta função ainda éint
como passado no parâmetro type masint?
se traduz em um tipo totalmente diferente:Nullable<int>
.