Тернарный оператор VB против С#: почему не разрешает ничего нулю?

Я просто стреляю себе в ногу и хотел бы знать, существуют ли реальные причины, чтобы сделать эту ситуацию возможной.
И вообще, этот вопрос может остаться для удобства будущих стрелков ног.


Предположим, что мы имеем значение nullable в vb.net:

Dim i as Integer?

Мы хотим присвоить ему значение, основанное на условии, и используя тернарный оператор, потому что он настолько опрятен и прочен:

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

То есть, если условие true, используйте значение nullability, в противном случае значение.
В этот момент происходит стрельба. Без видимой причины компилятор VB решает, что общий базовый тип для Nothing и Integer равен Integer, после чего он молча переводит оператор в:

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

Теперь, если вы должны сделать это на С#:

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

Вы сразу получите ошибку компилятора, говоря, что <null> не хорошо сочетается с int. Что здорово, так как моя нога была бы здоровее, если бы я отправился на С# на этот раз. И для этого, чтобы скомпилировать, вы должны явно написать:

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

Теперь вы можете сделать то же самое в VB и получить нулевое значение, которое вы ожидаете:

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

Но это требует, чтобы ваша нога была сделана в первую очередь. Там нет ошибки компилятора и нет предупреждения. Это с Explicit On и Strict On.


Итак, мой вопрос: почему? Должен ли я воспринимать это как ошибку компилятора?
Или может кто-нибудь объяснить, почему компилятор ведет себя таким образом?

Ответ 1

Это связано с тем, что VB Nothing не является прямым эквивалентом С# null.

Например, в С# этот код не будет компилироваться:

int i = null;

Но этот код VB.Net работает отлично:

Dim i As Integer = Nothing

VB.Net Nothing на самом деле является более близким совпадением для выражения С# default(T).

Ответ 2

Тернарный оператор может возвращать только один тип.

В С# он пытается выбрать тип, основанный на null и 42. Ну, null не имеет типа, поэтому он решает, что возвращаемый тип тернарного оператора равен типу 42; простой старый int. Затем он жалуется, потому что вы не можете вернуть null как простой старый int. Когда вы принуждаете 42 как int?, тернарный оператор возвращает int?, поэтому null действителен.

Теперь я не знаю о VB, но цитирую из MSDN,
Assigning Nothing to a variable sets it to the default value for its declared type.

Что, поскольку VB определяет, что тернарный оператор вернет int (используя тот же процесс С#), Nothing есть 0. Опять же, принуждение 42 к int? превращает Nothing в значение по умолчанию int?, которое, как вы ожидали, null.

Ответ 3

Nothing и null - это не одно и то же... из MSDN:

Присвоение ничего переменной присваивает значение по умолчанию для объявленного типа.

Кроме

Если вы указываете тип значения в выражении, IsNothing всегда возвращает False.

Имейте в виду, что int? является типом с нулевым значением, но он все еще является типом значения, а не ссылочным типом.

Попробуйте установить DbNull.Value вместо Nothing...

Ответ 4

Я думаю, что это имеет больше общего с IF, чем с Nothing. Рассмотрим этот код:

''#     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)

Почему он это делает, я до сих пор не знаю, вероятно, вопрос для команды VB. Я не думаю, что это связано с ключевым словом Nothing или Nullable.

Ответ 5

В ряде случаев Nothing преобразуется в значение по умолчанию. Чтобы использовать Nothing так же, как вы бы использовали null, вам нужно применить его к нулевому типу.

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))

Ответ 6

Это происходит потому, что Integer не является ссылочным типом. "Ничто" должно работать только для ссылочных типов. Для типов значений назначение Nothing автоматически преобразуется в значение по умолчанию, которое имеет место в случае Integer 0.

Ответ 7

В настоящее время это возможно в VS2015 (по крайней мере) с помощью New Integer?

Пример:

Если (testInt > 0, testInt, New Integer?), где testInt имеет тип Integer?