18

我们这些在 VB/VB.NET 中工作过的人都见过类似这种可憎的代码:

Dim name As String = IIf(obj Is Nothing, "", obj.Name)

我说“可憎”有三个简单的原因:

  1. IIf是一个函数,其所有参数都被评估;因此,如果obj上述调用中没有任何内容,NullReferenceException则将抛出 a。对于习惯于在 C# 等语言中使用短路三元运算符的人来说,这是出乎意料的行为。
  2. 因为 IIf是一个函数,所以它会产生函数调用的开销。同样,虽然这没什么大不了的,但对于那些期望它表现为语言固有的三元运算的人来说,它只是感觉不对。
  3. IIf是非泛型的,因此接受 type 的参数Object,这意味着以下调用框(我相信)总共三个整数:

    ' boxes 2nd and 3rd arguments as well as return value '
    Dim value As Integer = IIf(condition, 1, -1)

现在,在一些更新的 VB.NET 版本中(我不确定数字是多少),If引入了运算符,它的工作方式与函数完全相同,IIf但(据我了解)没有相同的缺点。也就是说,它确实提供了短路,它一种内在的 VB 操作。但是,我不确定最后一部分。MSDN 文档似乎没有表明是否将If其参数框起来。有人知道吗?

4

2 回答 2

12

主要的是您正确地将 new 标识If运算符而不是函数。它也是类型安全的,因此不需要装箱,并且直接映射到条件/三元/?C/C++/C#/Java/等中的运算符

即使没有 new 运算符,您也可以使用以下代码在 VB.Net 中获得一些改进:

Public Shared Function IIf(Of T)(ByVal Expression As Boolean, ByVal TruePart As T, ByVal FalsePart As T) As T
    If Expression Then Return TruePart Else Return FalsePart
End Function
于 2010-03-09T14:58:01.453 回答
11

Joel 打败了我,但这里有一个示例程序和生成的 IL,它演示 If() 在没有装箱的情况下传递到 IL 的底层三元运算符。

Public Class Test
    Public Sub New()
        Dim rnd = New Random()
        Dim result As Integer = If(rnd.Next(1000) < 500, 1, -1)
        Console.WriteLine(result)
    End Sub
End Class

如您所见,IL 没有“框”语句。

.method public specialname rtspecialname instance void .ctor() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 result,
        [1] class [mscorlib]System.Random rnd)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: call instance void [mscorlib]System.Object::.ctor()
    L_0007: nop 
    L_0008: newobj instance void [mscorlib]System.Random::.ctor()
    L_000d: stloc.1 
    L_000e: ldloc.1 
    L_000f: ldc.i4 0x3e8
    L_0014: callvirt instance int32 [mscorlib]System.Random::Next(int32)
    L_0019: ldc.i4 500
    L_001e: blt.s L_0023
    L_0020: ldc.i4.m1 
    L_0021: br.s L_0024
    L_0023: ldc.i4.1 
    L_0024: stloc.0 
    L_0025: ldloc.0 
    L_0026: call void [mscorlib]System.Console::WriteLine(int32)
    L_002b: nop 
    L_002c: nop 
    L_002d: ret 
}

给定相同的程序但使用较旧的 IIf() 函数,生成以下 IL。您可以看到装箱和函数调用开销:

.method public specialname rtspecialname instance void .ctor() cil managed
{
    .maxstack 3
    .locals init (
        [0] int32 result,
        [1] class [mscorlib]System.Random rnd)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: call instance void [mscorlib]System.Object::.ctor()
    L_0007: nop 
    L_0008: newobj instance void [mscorlib]System.Random::.ctor()
    L_000d: stloc.1 
    L_000e: ldloc.1 
    L_000f: ldc.i4 0x3e8
    L_0014: callvirt instance int32 [mscorlib]System.Random::Next(int32)
    L_0019: ldc.i4 500
    L_001e: clt 
    L_0020: ldc.i4.1 
    L_0021: box int32
    L_0026: ldc.i4.m1 
    L_0027: box int32
    L_002c: call object [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::IIf(bool, object, object)
    L_0031: call int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToInteger(object)
    L_0036: stloc.0 
    L_0037: ldloc.0 
    L_0038: call void [mscorlib]System.Console::WriteLine(int32)
    L_003d: nop 
    L_003e: nop 
    L_003f: ret 
}
于 2010-03-09T15:17:36.297 回答