为什么VB.NET中的三元运算符可以接受可空布尔值?

3
以下内容在VB.NET中编译(使用Option Strict On)并输出False:
Dim b As Boolean? = Nothing
Dim myString = If(b, "True", "False")

为什么那样可以工作?
  • The documentation clearly states that the three-argument version of If requires a Boolean as the first parameter:

    argument1 Required. Boolean. Determines which of the other arguments to evaluate and return.

  • and there is no implicit conversion from Boolean? to Boolean:

    Dim b1 As Boolean? = Nothing
    Dim b2 As Boolean = b1   ' Fails with the following error:
                             '   Option Strict On disallows implicit conversions
                             '   from 'Boolean?' to 'Boolean'.
    

那么,为什么这个方法有效呢?这是编译器中的一个错误(或者“隐藏特性”),还是文档中的一个错误,并且Boolean?实际上是If(a, b, c)第一个参数的有效类型?

PS:在C#中,如果b的类型为bool?,则b ? x : y无法编译。


编辑:我已经向Microsoft Connect报告了这个问题。微软的某位工作人员已回复并确认文档将会更新,包括Boolean?这种情况。

4个回答

4

这里有两个“为什么”。一个是它为什么会这样,另一个则是微软为什么要这样做。我可以回答第一个问题,而第二个问题只能由微软来回答。

如果您使用Reflector查看从VB.Net生成的代码,您会看到以下内容:

Dim b As Nullable(Of Boolean) = Nothing
Dim myString As String = IIf(b.GetValueOrDefault, "True", "False")

或者C#:

bool? b = null;
string myString = b.GetValueOrDefault() ? "True" : "False";

因此,编译器本身正在为可空类型Nullable(of T)插入GetValueOrDefault

2
它真的使用IIF吗?(注意有两个I) - Chris Dunaway
1
很好的发现。我的反编译器设置为 .Net 2.0,没有 VB 的 IF 三元运算符。更新到 4.0 后,它变成了 IF 而不是 IFF。显然这是反编译器试图通过 IL 重构 VB.Net。实际的 IL 命令是 brtrue.s,它是“如果为真则跳转”,通常被转换为三元运算符。 - Chris Haas

2

保留 ildasm.exe,以解决此类问题。编译器使用 Nullable(Of T).GetValueOrDefault()。官方语言规范不禁止此行为。也没有其他相关说明,这并不罕见。

  IL_0001:  ldloca.s   b
  IL_0003:  initobj    valuetype [mscorlib]System.Nullable`1<bool>
  IL_0009:  ldloca.s   b
  IL_000b:  call       instance !0 valuetype [mscorlib]System.Nullable`1<bool>::GetValueOrDefault()
  IL_0010:  brtrue.s   IL_0019
  etc...

2

Docos中指出:

使用三个参数调用的If运算符类似于IIf函数,但它使用短路求值。IIf函数总是计算其三个参数,而具有三个参数的If运算符仅计算其中两个。首先评估第一个If参数,并将结果转换为布尔值True或False。

它会进行评估然后转换。

EDIT1

有趣的部分在于,在运行时,强制转换不会引发异常。


1
第一个 If 参数被评估并将结果转换为布尔值,这点你指出得很好!有趣的是,它似乎并不总是正确的:If(0, 1, 2) 抛出 Option Strict On disallows implicit conversions from 'Integer' to 'Boolean'.,因此第一个参数并不总是被转换为布尔值。 - Heinzi

0

在处理可空类型时,应始终使用.HasValue属性而不是仅引用可空类型。VB在检查对象是否已实例化时有使用以下语法的传统:

If Foo Then
  ' Is instantiated
End If

因此,你的例子允许对三元条件进行求值。在这种情况下,我建议“修正”你的代码,使其更加明确:

Dim b As Boolean? = Nothing 
Dim myString = If(b.HasValue, "True", "False") 

或者,不使用三元运算符,为什么不将其重写为:

Dim myString = b.GetValueOrDefault(False).ToString()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接