Тернарный оператор VB против C #: почему ничего не решает ноль? - PullRequest
24 голосов
/ 10 ноября 2010

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


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

.
Dim i as Integer?

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

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

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

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

Теперь, если бы вы сделали это в C #:

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

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

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

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

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

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


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

Ответы [ 7 ]

34 голосов
/ 10 ноября 2010

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

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

int i = null;

Но этот код VB.Net прекрасно работает:

Dim i As Integer = Nothing

VB.Net Nothing на самом деле более близко соответствует выражению C # 1012 *.

12 голосов
/ 10 ноября 2010

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

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

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

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

2 голосов
/ 10 ноября 2010

Я думаю, что это как-то больше связано с 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.

2 голосов
/ 10 ноября 2010

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

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

Также

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

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

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

1 голос
/ 29 июня 2013

В ряде случаев Nothing будет преобразовано в значение по умолчанию.Чтобы использовать Nothing так же, как вы используете null, вам нужно привести его к правильному типу, допускающему значение 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))
0 голосов
/ 07 декабря 2017

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

Пример .:

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

0 голосов
/ 10 ноября 2010

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

...