Question

Those of us who've worked in VB/VB.NET have seen code similar to this abomination:

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

I say "abomination" for three simple reasons:

  1. IIf is a function, all of whose parameters are evaluated; hence if obj is nothing in the above call then a NullReferenceException will be thrown. This is unexpected behavior for someone who's accustomed to short-circuited ternary operators in languages like C#.
  2. Because IIf is a function, it thus incurs the overhead of a function call. Again, though this isn't a big deal, it just doesn't feel right to someone who expects for it to behave as a ternary operation intrinsic to the language.
  3. IIf is non-generic and therefore accepts parameters of type Object, which means the following call boxes (I believe) a total of three integers:

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

Now, in some more recent version of VB.NET (I'm not sure what the number is), the If operator was introduced, which works exactly the same way as the IIf function but (as I understand it) without the same shortcomings. That is to say, it does provide short-circuiting and it is an intrinstic VB operation. However, I'm not sure about the last part. The MSDN documentation doesn't seem to indicate whether If boxes its arguments or not. Does anyone know?

Was it helpful?

Solution

The main thing is that you correctly identified the new If as an operator rather than a function. It is also typesafe and therefore does not need boxing, and is a direct mapping to the conditional/ternary/? operator in C/C++/C#/Java/etc

Even without the new operator, you can get some improvement in VB.Net with this code:

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

OTHER TIPS

Joel beat me to an answer, but here is a sample program and the generated IL that demonstrates that If() passes through to the IL's underlying ternary operator without boxing.

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

As you can see the IL has no 'box' statement.

.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 
}

Given the same program but using the older IIf() function, the following IL is produced. You can see both the boxing, and the function call overhead:

.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 
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top