2011-01-26 4 views
7

Исходя из Basic boolean logic in C#, мне было интересно, почему:VB.NET: Boolean из `` Nothing` иногда false`, иногда Nullreference-Exception

Dim b As Boolean 
Dim obj As Object = Nothing 
'followig evaluates to False' 
b = DirectCast(Nothing, Boolean) 
'This throws an "Object reference not set to an instance of an object"-Exception' 
b = DirectCast(obj, Boolean) 

CType(obj, Boolean) будет вычисляться False (так же, как CBool(obj)). Я думаю, это потому, что компилятор использует вспомогательную функцию, но это не моя тема.

Почему кастинг Nothing в Boolean вычисляет False, в то время как отливка объекта, Nothing - Boolean выбрасывает исключение Nullreference? Имеет ли это смысл?

[Option Strict ON] 
+0

Некоторые ответы здесь относятся к фреймворку 2.0, это около 3,5. существует нулевой тип в 3.5 для VB – Fredou

+0

@Fredou: ответ на этот вопрос не зависит от конкретной версии Framework, а типы с нулевым значением не являются решением. Ответчик не ищет * решение *, а вместо * объяснения *, почему код ведет себя так же, как и он. –

ответ

13

Предположительно, это происходит потому, что Nothing в VB.NET это не совсем то же самое, как null в C#.

В случае типов значений Nothing подразумевает значение по умолчанию этого типа. В случае Boolean значением по умолчанию является False, поэтому листинг преуспевает.

Одно из основных различий между типами значений, такими как Integer или структуры, и ссылочными типами, такими как Form или String, является то, что ссылочные типы поддерживают нулевое значение. То есть переменная ссылочного типа может содержать значение Nothing, а это означает, что переменная фактически не ссылается на значение. Напротив, переменная типа значения всегда содержит значение. Целочисленная переменная всегда содержит число, даже если это число равно нулю. Если вы присваиваете значение Nothing переменной типа значения, переменной типа значения присваивается ее значение по умолчанию (в случае Integer это значение по умолчанию равно нулю). В текущем CLR нет способа посмотреть переменную Integer и определить, было ли ей никогда не назначено значение - тот факт, что он содержит ноль, не обязательно означает, что ему не было присвоено значение.
        - The Truth about Nullable Types and VB...

РЕДАКТИРОВАТЬ: Для дальнейшего уточнения, причина второй пример бросает NullReferenceException во время выполнения происходит потому, что CLR пытается распаковывать в Object (ссылочный тип), чтобы a Boolean. Это не может, конечно, так как объект был инициализирован с нулевой ссылки (установив его равным Nothing):

Dim obj As Object = Nothing 

Помните, что, как я уже говорил выше, VB.NET ключевого слова Nothing все еще работает так же, как null в C#, когда речь идет о ссылочных типах. Это объясняет, почему вы получаете NullReferenceException, потому что объект, который вы пытаетесь выполнить, буквально является пустой ссылкой. Он не содержит значения вообще и поэтому не может быть распакован в Boolean.

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

Dim b As Boolean = DirectCast(Nothing, Boolean) 

, потому что ключевое слово Nothing (на этот раз, в случае типов значений) просто означает «значение по умолчанию этого типа». В случае Boolean это False, поэтому литье является логичным и простым.

+0

Спасибо. Но до сих пор не указано, почему исключение Nullreference будет выбрано во время выполнения, если я попытаюсь применить объект, который не является логическим. Он также может быть изменен на false, поскольку значение по умолчанию для логического значения является ложным. –

+0

@ Тим: Нет, не мог. По крайней мере, не обязательно. Помните, что 'Object' является * ссылочным типом *, а не типом значения. Как я уже объяснял, ключевое слово «Nothing» по-прежнему работает так же, как «null» в C#, когда речь идет о типах ссылок. Когда вы устанавливаете переменную типа «Object» в «Nothing», это означает, что она не содержит значения. –

+0

@ Кодировка серого: это означает, что CLR ищет, является ли объект типом значения или ссылочным типом. Если последний он напрямую выдает исключение NullReferenceException, если он должен что-то с ним делать (например, casting). Если я пытаюсь использовать 'Nothing' для логического, компилятор неявно назначает ему« False », поэтому CLR не будет генерировать исключение, потому что кастинг не выполняется во время выполнения, а во время компиляции. Верный? –

2

Там разница между использованием ключевого слова (буквальный) Nothing, и используя ссылку переменной, значение которого Nothing.

  • В VB.NET, буквальный (ключевое слово) Nothing получает специальную обработку. Nothingключевое слово может быть автоматически преобразовано в тип значения, используя значение по умолчанию этого типа.

  • Ссылка переменная, значение которой Nothing отличается. Вы не получаете особого поведения.

  • В документации указано, что DirectCast «требует отношения наследования или реализации между типами данных двух аргументов».

  • Понятно, что Object не наследует или не реализует Boolean, если только вы не поместили коробку Boolean в переменную объекта.

Таким образом, приведенный ниже код не работает во время выполнения с исключением.

Dim obj As Object = Nothing 
b = DirectCast(obj, Boolean) 
+0

Как я уже упомянул в конце своего вопроса, у меня есть опция «Strict», но она не работает во время выполнения. Посмотрите мой последний комментарий на ответ Коди Грея. Спасибо, в любом случае. –

+3

Этот ответ абсолютно прав, за исключением последней части, которая, как отмечает Тим, не связана с проблемой. ('Option Strict' не предотвратит вас от downcasting' Object' до 'Boolean', что всегда * может * работать, если предположить, что это бокс' Boolean'!) –

+0

@Tim @Dan К сожалению, я удалю это последний бит. Да и +1 к ответу Дэна тоже. – MarkJ

1

Чтобы получить ожидаемое поведение, вам нужен этот код:

Dim b As Boolean? 
Dim obj As Object = Nothing 
b = DirectCast(obj, Boolean?) 

Символ ?Nullable(of) значит.

+0

Было бы ожидаемым поведением, если бы я получил InvalidCast-Exception в compiletime. Это был еще более теоретический вопрос, почему компилятор vb.net ведет себя отличным от C#. Спасибо за подсказку, я перейду на VS 2010 на следующей неделе, тогда я смогу использовать этот синтаксис. –

4

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

первый является то, что другие уже указывали: Nothingможет интерпретироваться VB компилятор просто как значения FalseBoolean с учетом надлежащего контекста, например, Dim b As Boolean = Nothing.

Это означает, что когда компилятор видит это:

b = DirectCast(Nothing, Boolean) 

Он видит буквальное (Nothing), а также видит, что вы хотите использовать этот литерал как Boolean. Это делает его без проблем.

Но теперь вот второй вещь, которую вы должны реализовать. DirectCast на Object является, по сути, операцией unboxing (для типов значений).Так что нуждается в, чтобы произойти с точки зрения компилятора VB: там нуждается в, чтобы быть Boolean в этом поле, иначе операция завершится с ошибкой. Поскольку на самом деле есть ничего в коробке, и на этот раз мы действительно говорим ничего, как в null -it выдает исключение.

Если бы я перевести этот код на C#, она будет выглядеть следующим образом:

bool b; 
object obj = null; 

b = (bool)default(bool); 

b = (bool)obj; 

Надеется, что делает вещи немного яснее?

+0

+1 Хотя это незначительный каламбур. Я не уверен в формулировке «мы действительно говорим« ничего », когда (я думаю) вы имеете в виду ссылочную переменную, содержащую« ничего », а не буквальный« ничего ». Разумеется, буквальное «Ничто» не так реально, как получается? Во всяком случае, это лучший ответ, чем мой ... – MarkJ

0

Я нахожу, что сравнение булевой переменной с строкой «True», «False» или ничего не гарантирует, что я получаю правильные сравнения. Я использовал функцию, чтобы вернуть строку html div с изображением отмеченного или непроверенного переключателя, и проблема в том, что ничего не возвращается как ложное. Используя переменную = «True» или «False», и последняя проверка с помощью IS NOTHING помогла решить эту проблему.

Dim b as boolean = nothing 

response.write CheckValue(b = "True") 
response.write (b = "False") 
response.write (b is nothing) 

Function CheckValue(inVal as boolean) as string 
    if inVal then 
    return ("<div><img src="checked.png" ></div> 
    else 
    return ("<div><img src="unchecked.png" ></div>  
    end if 
end function 

система, кажется, делает преобразование в строку, когда неявно по сравнению с строкой, тогда как с помощью .ToString метод просто создает ошибку, позволяя последнее сравнение фактически сравнить со значением ничего не принимается.

Надеюсь, это поможет. Это, по крайней мере, дайте мне

Смежные вопросы