2013-04-18 23 views
5

natknąłem się dzisiaj na dziwne zachowanie instrukcji VB.net If(). Może możesz wyjaśnić, dlaczego działa tak, jak działa, lub możesz potwierdzić, że to błąd.Dziwne zachowanie instrukcji If()

Mam bazę danych SQL z tabelą "TestTable" z kolumną int "NullableColumn", która może zawierać wartość NULL. Chciałbym przeczytać zawartość tej kolumny.

więc zadeklarować zmienną typu Nullable(Of Integer) dla tej sprawy, otwórz SqlClient.SqlDataReader dla „SELECT NullableColumn OD TestTable” i użyć następującego kodu, aby uzyskać zawartość tej kolumny:

Dim content as Nullable(Of Integer) 

... 

Using reader as SqlClient.SqlDataReader = ... 
    content = If(reader.IsDBNull(reader.GetOrdinal("NullableColumn")), Nothing, reader.GetInt32(reader.GetOrdinal("NullableColumn"))) 
End Using 

Ale potem mój zmienna content ma wartość 0, a nie Nothing, tak jak bym się spodziewał.

Gdy debugowanie wszystko wygląda w porządku, więc

  • reader.GetOrdinal("NullableColumn") zapewnia prawidłową porządkowej pozycji tej kolumnie (która jest 0)
  • reader.IsDBNull(0) i reader.IsDBNull(reader.GetOrdinal("NullableColumn")) dostarczyć True, ponieważ treść tej kolumnie rzeczywiście jest NULL
  • If(1=2, Nothing, "Not Nothing") dostarcza ciąg "Not Nothing"
  • If(1=1, Nothing, "Not Nothing") dostarcza Nothing
  • reader.GetInt32(reader.GetOrdinal("NullableColumn")) zgłasza błąd, ponieważ NULL nie mogą być konwertowane do Integer

Więc dlaczego moja zmienna ma wartość 0?

+0

Czy stosować normalne IF - następnie - elese oświadczenie? Czy to działa? –

+0

Ciekawe, czy włączony jest "Option Strict"? Jeśli nie, powinieneś –

+0

Tak, spróbowałem normalnego 'If ... Then ... Else ...' statemant, i to działało. Włączenie 'Option Strict' nic nie zmieniło. – Nostromo

Odpowiedz

5

W języku VB Nic nie jest takie samo jak zero. Operator If musi określić typ jego wyniku na podstawie przekazanych mu argumentów. Nic oczywiście nie ma typu, więc jedynym typem, który operator If może zwrócić w kodzie jest Int32. Jeśli metoda IsDBNull zwraca true, wówczas operator If zwraca Nothing cast jako Int32. W VB, Nothing zwraca wartość domyślną dla typu. W przypadku Int32 domyślną wartością jest 0.

Od MSDN na słowa kluczowego Nothing:

Nothing represents the default value of a data type. The default value depends 
on whether the variable is of a value type or of a reference type. 

For non-nullable value types, Nothing in Visual Basic differs from null in C#. 
In Visual Basic, if you set a variable of a non-nullable value type to Nothing, 
the variable is set to the default value for its declared type. In C#, if you 
assign a variable of a non-nullable value type to null, a compile-time error 
occurs. 

myślę zwykłym Jeśli będzie działać najlepiej:

If Not reader.IsDBNull(reader.GetOrdinal("NullableColumn")) Then 
    content = reader.GetInt32(reader.GetOrdinal("NullableColumn")) 
End If 

lub utrzymanie go w krótszym

If Not reader.IsDBNull(reader.GetOrdinal("NullableColumn")) Then content = reader.GetInt32(reader.GetOrdinal("NullableColumn")) 
1

Ale po tym moja zmienna content ma wartość 0 nie Nothing jak oczekiwałem.

Jak sprawdzić wartość content? Najpierw należy zacząć od właściwości . Powinien to być numer False dla Twojego przypadku: Nothing i True, gdy została pobrana prawidłowa wartość z bazy danych.

Powinieneś również uzyskać InvalidOperationException podczas uzyskiwania dostępu do content.Value, gdy nie ma on wartości.

+0

Przesunięcie wskaźnika myszy nad zmienną w trybie debugowania powoduje wyświetlenie zawartości tej zmiennej. Null typu obiektu pokazuje "Nic", jeśli nie ma wartości i rzeczywistej wartości, jeśli tak. Jeśli używam zwykłego wyrażenia 'If ... Then ... Else ...', Intellisense pokazuje 'Nic', jeśli użyję statutu' If (...) ', Intellisense pokazuje 0. – Nostromo

1

Chris dał wyjaśnienie, ale nie podoba mi się styl przypisania, który wybrał, ponieważ dzieli zadanie od zmiennej deklarati na.

W zamian za to zalecam inicjowanie zmiennych po deklaracji. W tym przypadku jest to wprawdzie nieco zawiłe, ponieważ trzeba najpierw odseparować operatora od If do poprawnego typu.

Dim content = If(reader.IsDBNull(reader.GetOrdinal("NullableColumn")), 
       DirectCast(Nothing, Integer?), 
       reader.GetInt32(reader.GetOrdinal("NullableColumn"))) 

Właściwie można również użyć nieco krótsza New Integer?() zamiast DirectCast.

Oczywiście teraz content jest zadeklarowana wewnątrz bloku Using - to nie może być to, co chcesz, ale powinien spróbować do sporządzania deklaracji jako lokalne, jak to możliwe.

Co więcej, kod ten jest złożony i prawdopodobnie zostanie ponownie użyty. Proponuję stworzenie odrębnej metody (dodatek) do konwersji wartości NULL bazy danych do nullables:

<Extension> _ 
Public Shared Function GetNullable(Of T)(SqlClient.SqlDataReader this, String fieldName) As T? 
    Dim i = this.GetOrdinal(fieldName) 
    Return If(this.IsDBNull(i), New T?(), this.GetFieldValue(Of T)(i)) 
End Function 

Teraz można go używać w następujący sposób:

Dim content = reader.GetNullable(Of Integer)("NullableColumn")