2013-04-18 32 views
5

oggi mi sono imbattuto in uno strano comportamento dell'istruzione VB.net If(). Forse puoi spiegare perché funziona come fa, o forse puoi confermare che si tratta di un bug.Strano comportamento dell'istruzione If()

Quindi, ho un database SQL con una tabella "TestTable" con una colonna int "NullableColumn" che può contenere NULL. Mi piacerebbe leggere il contenuto di questa colonna.

Quindi dichiaro una variabile di tipo Nullable(Of Integer) per quella materia, aprire un SqlClient.SqlDataReader per "SELECT NullableColumn DA TestTable" e utilizzare il seguente codice per ottenere il contenuto di questa colonna:

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 

Ma dopo che il mio variabile content ha il valore 0, non Nothing come mi sarei aspettato.

Quando il debug tutto sembra a posto, in modo da

  • reader.GetOrdinal("NullableColumn") batte corretta posizione ordinale di questa colonna (che è 0)
  • reader.IsDBNull(0) e reader.IsDBNull(reader.GetOrdinal("NullableColumn")) fornire True, dal momento che il contenuto di questa colonna in effetti è NULL
  • If(1=2, Nothing, "Not Nothing") batte lo stringa "Not Nothing"
  • If(1=1, Nothing, "Not Nothing") trasporta Nothing
  • reader.GetInt32(reader.GetOrdinal("NullableColumn")) genera un errore, dal momento che NULL non può essere convertito in Integer

Quindi, perché fa il mio variabile ha il valore 0?

+0

Avete usato un normale Se - dichiarazione elese - allora? Funziona? –

+0

Per motivi di interesse hai attivato Option Strict? Se no dovresti farlo –

+0

Sì, ho provato un normale 'Se ... Allora ... Altrimenti ... 'statemant, e questo ha funzionato. L'attivazione di Option Strict non ha cambiato nulla. – Nostromo

risposta

5

In VB Niente è diverso da null. L'operatore If deve determinare il tipo del suo risultato in base agli argomenti passati. Niente, ovviamente, non ha alcun tipo quindi l'unico tipo che l'operatore If può restituire nel tuo codice è Int32. Se il metodo IsDBNull restituisce true, l'operatore If restituisce Nothing cast come Int32. In VB, Nothing restituisce il valore predefinito per un tipo. Per un Int32, il valore predefinito è 0.

da MSDN sulla parola chiave Niente:

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. 

Penso solo un caso normale avrebbe funzionato meglio:

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

o per mantenere più breve

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

Ma dopo che la mia variabile content ha il valore 0, non Nothing come mi sarei aspettato.

Come si controlla il valore content?

Prima di tutto, è necessario iniziare con la proprietà content.HasValue. Dovrebbe essere False per il tuo caso di Nothing e True quando il valore corretto è stato recuperato dal database.

Si dovrebbe anche ottenere InvalidOperationException mentre si accede a content.Value quando non ha valore.

+0

Se metto il mouse su una variabile in modalità debug vedo il contenuto di questa variabile. Un tipo di oggetto nullable mostra 'Nothing', se non ha alcun valore e il valore effettivo, se lo fa. Se uso una normale istruzione 'If ... Then ... Else ...', l'Intellisense mostra 'Nothing', se uso lo statemante' If (...) ', l'Intellisense mostra 0. – Nostromo

1

Chris ha dato la spiegazione, ma non mi piace lo stile di incarico che ha scelto perché divide l'incarico dalla variabile dichiarati sopra.

Al contrario, consiglio di inizializzare le variabili dopo la dichiarazione. In questo caso è certamente leggermente contorto perché è necessario eseguire il cast dell'operatore di If nel tipo corretto per primo.

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

In realtà si può anche utilizzare il leggermente più corta New Integer?() al posto del DirectCast.

Naturalmente ora content viene dichiarata all'interno del blocco Using - questo potrebbe non essere ciò che si vuole, ma si dovrebbe cercare di rendere la dichiarazione locale come possibile.

Inoltre, questo codice è complesso e verrà probabilmente riutilizzato. Suggerisco di creare un metodo separato (estensione) per convertire i valori NULL database per 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 

Ora è possibile utilizzarlo come segue:

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