Тернарный оператор 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?