Operador ternário VB vs C #: por que resolve Nothing to zero?

Eu só me fotografo no pé e gostaria de saber se havia razões reais para tornar essa situação possível.
E de qualquer forma, esta questão pode ficar para a conveniência dos futuros atiradores de pé.


Suponha que tenhamos um valor anulável em vb.net:

Dim i as Integer? 

Queremos atribuir um valor a ele, baseando-se em uma condição e usando um operador ternário, porque é tão legal e interessante:

 i = If(condition(), Nothing, 42) 

Ou seja, se uma condição for true , empregue a capacidade de anulação, caso contrário, o valor.
Em que ponto o tiroteio ocorre. Por nenhuma razão aparente, o compilador VB decide que o tipo de base comum para Nothing e Integer é Integer , momento em que silenciosamente converte a instrução para:

 i = If(condition(), 0, 42) 

Agora, se você fosse fazer isso em C #:

 i = (condition()) ? null : 42; 

Você obteria imediatamente um erro do compilador dizendo que não combina bem com int . O que é ótimo, já que meu pé teria sido mais saudável se eu tivesse feito o caminho C # dessa vez. E para isso compilar, você tem que escrever explicitamente:

 i = (condition()) ? null : (int?)42; 

Agora, você pode fazer o mesmo em VB e obter a nulidade correta esperada:

 i = If(condition(), Nothing, CType(42, Integer?)) 

Mas isso requer ter seu pé triggersdo em primeiro lugar. Não há erro no compilador e não há aviso. Isso é com o Explicit On e o Strict On .


Então minha pergunta é: por quê?
Devo tomar isso como um erro de compilador?
Ou alguém pode explicar por que o compilador se comporta dessa maneira?

Isso ocorre porque Nothing do VB não é um equivalente direto ao null C #.

Por exemplo, em c # este código não irá compilar:

 int i = null; 

Mas este código VB.Net funciona muito bem:

 Dim i As Integer = Nothing 

Nothing do VB.net é, na verdade, uma correspondência mais próxima para a expressão default(T) C #.

O operador ternário só pode retornar um tipo.

Em C #, ele tenta escolher um tipo baseado em null e 42 . Bem, null não tem um tipo, então ele decide que o tipo de retorno do operador ternário é o de 42 ; um int velho e simples. Em seguida, ele reclama porque você não pode retornar null como um antigo int simples. Quando você coage 42 como um int? , o operador ternário vai retornar um int? , então null é válido.

Agora, eu não sei sobre o VB, mas citando o MSDN,
Assigning Nothing to a variable sets it to the default value for its declared type.

Que, como o VB determina que o operador ternário retornará um int (usando o mesmo processo que o C # fez), Nothing é 0 . Novamente, coagindo o 42 para ser um int? transforma Nothing no valor padrão de int? , que é null , como você esperava.

Eu estou pensando que isso tem algo mais a ver com IF do que com Nothing. Considere este código:

 ''# This raises an exception Dim x As Integer? x = If(True, Nothing, Nothing) MessageBox.Show(x.Value) ''# As does Dim x As Integer? x = Nothing MessageBox.Show(x.Value) ''# Changing one of the truthpart arguments of If is what seems to return the zero. Dim x As Integer? x = If(True, Nothing, 5) MessageBox.Show(x.Value) 

Por que isso está fazendo isso eu ainda não sei, provavelmente uma pergunta para a equipe do VB. Eu não acho que isso tenha a ver com a palavra-chave Nothing ou Nullable.

Nothing e null não são a mesma coisa … no MSDN:

Atribuir Nothing a uma variável define o valor padrão para seu tipo declarado.

Além disso

Se você fornecer um tipo de valor em Expressão, IsNothing sempre retornará False.

Tenha em mente que int? é um tipo anulável, mas ainda é um tipo de valor, não um tipo de referência.

Tente defini-lo como DbNull.Value vez de Nothing

Em vários casos, Nothing será convertido para o valor padrão. Para usar o Nothing da mesma maneira que você usaria null você precisa convertê-lo para o tipo anulável correto.

 Dim str As String Dim int As Nullable(Of Integer) ' or use As Integer? Dim reader As SqlDataReader Dim colA As Integer = reader.GetOrdinal("colA") Dim colB As Integer = reader.GetOrdinal("colB") str = If(reader.IsDBNull(colA), DirectCast(Nothing, String), reader.GetString(colA)) int = If(reader.IsDBNull(colB), DirectCast(Nothing, Nullable(Of Integer)), reader.GetInt32(colB)) 

Isso acontece porque Integer não é um tipo de referência. ‘Nada’ só deve funcionar para tipos de referência. Para tipos de valor que atribuem Nothing é automaticamente convertido para o valor padrão, que é no caso de um Integer 0.

Isso é realmente possível agora no VS2015 (no mínimo) usando New Integer?

Ex.:

Se (testInt> 0, testInt, New Integer?), Onde testInt é do tipo Integer?