Does the VB.NET "If" operator cause boxing?
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:
IIf
is a function, all of whose parameters are evaluated; hence ifobj
is nothing in the above call then aNullReferenceException
will be thrown. This is unexpected behavior for someone who's accustomed to short-circuited ternary operators in languages like C#.- 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. IIf
is non-generic and therefore accepts parameters of typeObject
, 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 wha开发者_如何学JAVAt 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?
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
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
}
精彩评论