5

今天我偶然发现了 VB.net If() 语句的一个奇怪行为。也许您可以解释为什么它会像现在这样工作,或者您可以确认它是一个错误。

所以,我有一个 SQL 数据库,其中有一个表“TestTable”,其中一个 int 列“NullableColumn”可以包含 NULL。我想读出本专栏的内容。

所以我为此声明了一个类型的变量,为“SELECT NullableColumn FROM TestTable”Nullable(Of Integer)打开一个SqlClient.SqlDataReader并使用以下代码获取该列的内容:

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

但在那之后,我的变量content的值为 0,Nothing与我预期的不同。

调试时一切看起来都很好,所以

  • reader.GetOrdinal("NullableColumn")提供此列的正确序号位置(即 0)
  • reader.IsDBNull(0)reader.IsDBNull(reader.GetOrdinal("NullableColumn"))交付True,因为该列的内容确实为 NULL
  • If(1=2, Nothing, "Not Nothing")提供字符串“Not Nothing”
  • If(1=1, Nothing, "Not Nothing")交付Nothing
  • reader.GetInt32(reader.GetOrdinal("NullableColumn"))抛出错误,因为 NULL 不能转换为Integer

那么,为什么我的变量的值为 0?

4

3 回答 3

5

在 VB 中,Nothing 与 null 不同。If 运算符必须根据传递给它的参数确定其结果的类型。当然,Nothing 没有类型,因此 If 运算符可以在您的代码中返回的唯一类型是 Int32。如果 IsDBNull 方法返回 true,则 If 运算符返回 Nothing 转换为 Int32。在 VB 中,Nothing 返回类型的默认值。对于 Int32,默认值为 0。

从 MSDN 关于 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.

我认为只是一个普通的 If 效果最好:

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

或者让它更短

If Not reader.IsDBNull(reader.GetOrdinal("NullableColumn")) Then content = reader.GetInt32(reader.GetOrdinal("NullableColumn"))
于 2013-04-18T15:11:23.863 回答
1

但在那之后,我的变量content具有 value 0,而不是Nothing我所期望的。

你如何检查content价值?

首先,你应该从content.HasValue财产开始。它应该False适用于您的情况Nothing以及True从数据库中获取正确值的时间。

当它没有价值时,您也应该InvalidOperationException在访问时获得。content.Value

于 2013-04-18T08:40:26.820 回答
1

Chris 给出了解释,但我不喜欢他选择的赋值风格,因为它将赋值与变量声明分开。

相比之下,我建议在声明时初始化变量。在这种情况下,它确实有点令人费解,因为您需要If首先将任一运算符转换为正确的类型。

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

其实你也可以用稍短New Integer?()的代替DirectCast.

当然 nowcontent是在Using块内声明的——这可能不是你想要的,但你应该尽量使声明本地化。

此外,这段代码很复杂,很可能会被重用。我建议创建一个单独的(扩展)方法来将数据库 NULL 值转换为可空值:

<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

现在您可以按如下方式使用它:

Dim content = reader.GetNullable(Of Integer)("NullableColumn")
于 2013-04-18T15:32:16.783 回答