VB.NET是否;如果;操作员导致拳击?

VB.NET是否;如果;操作员导致拳击?,vb.net,ternary-operator,boxing,Vb.net,Ternary Operator,Boxing,我们这些曾经在VB/VB.NET中工作过的人看到过类似于这种讨厌的代码: Dim name As String = IIf(obj Is Nothing, "", obj.Name) 我说“讨厌”有三个简单的原因: IIf是一个函数,对其所有参数进行评估;因此,如果obj在上述调用中为空,则将抛出NullReferenceException。对于习惯于在C#等语言中使用短路三元运算符的人来说,这是一种意想不到的行为 因为IIf是一个函数,因此会产生函数调用的开销。再说一次,虽然这没什么大不了的

我们这些曾经在VB/VB.NET中工作过的人看到过类似于这种讨厌的代码:

Dim name As String = IIf(obj Is Nothing, "", obj.Name)
我说“讨厌”有三个简单的原因:

  • IIf
    是一个函数,对其所有参数进行评估;因此,如果
    obj
    在上述调用中为空,则将抛出
    NullReferenceException
    。对于习惯于在C#等语言中使用短路三元运算符的人来说,这是一种意想不到的行为
  • 因为
    IIf
    是一个函数,因此会产生函数调用的开销。再说一次,虽然这没什么大不了的,但对于那些期望它作为语言固有的三元操作的人来说,这是不对的
  • IIf
    是非泛型的,因此接受类型为
    Object
    的参数,这意味着以下调用框(我相信)总共包含三个整数:

    “框2和3参数以及返回值”

    Dim值为整数=IIf(条件,1,-1)


  • 现在,在VB.NET的最新版本中(我不确定数字是多少),引入了
    If
    操作符,它的工作方式与
    IIf
    函数完全相同,但(据我所知)没有相同的缺点。也就是说,它确实提供了短路,并且它是一个本质的VB操作。然而,我不确定最后一部分。似乎没有指示
    If
    是否将其参数装箱。有人知道吗?

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

    即使没有新的运算符,您也可以通过以下代码在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
    

    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
    
    给定相同的程序,但使用较旧的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 
    }
    

    我在VB.NET团队的一些开发人员(忘记是谁)的博客中注意到,他们实际上考虑过这样做——提供一个通用版本的
    IIf
    。我很高兴他们走上了他们所走的道路,因为通用版本可以解决装箱问题,但仍然不会提供短路(否则,将短路的内在语言特性伪装成通用方法将非常令人困惑)。
    .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 
    }