2010-03-09 8 views
18

Aquellos de nosotros que hemos trabajado en VB/VB.NET han visto un código similar a esta abominación:¿El operador "If" de VB.NET causa el boxeo?

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

digo "abominación" por tres razones simples:

  1. IIf es una función, todos sus parámetros son evaluados; por lo tanto, si obj no es nada en la llamada anterior, se lanzará un NullReferenceException. Este es un comportamiento inesperado para alguien que está acostumbrado a los operadores ternarios en cortocircuito en idiomas como C#.
  2. Dado queIIf es una función, se incurre en la sobrecarga de una llamada de función. De nuevo, aunque esto no es un gran problema, simplemente no se siente bien para alguien que espera que se comporte como una operación ternaria intrínseca al lenguaje.
  3. IIf es no genérico y por lo tanto acepta parámetros de tipo Object, lo que significa que las cajas de llamada siguientes (creo) un total de tres números enteros:

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

Ahora, en una versión más reciente de VB.NET (no estoy seguro de cuál es el número), se introdujo el operador If, que funciona exactamente de la misma manera que la función IIf pero (según tengo entendido) sin el mismo shor Tcomings. Es decir, hace proporcionar un cortocircuito y es una operación VB intrínseca. Sin embargo, no estoy seguro de la última parte. El MSDN documentation no parece indicar si If contiene sus argumentos o no. ¿Alguien sabe?

+0

+1 ¡Esta pregunta fue divertida! –

Respuesta

12

Lo principal es que identificó correctamente el nuevo If como operador en lugar de una función. También es seguro y, por lo tanto, no necesita boxeo, y es un mapeo directo al condicional/ternario /? operador en C/C++/C#/Java/etc

Incluso sin el nuevo operador, se puede conseguir una cierta mejora en VB.Net con este código:

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 
+0

Noté en el blog de algún desarrollador en el equipo de VB.NET (olviden quién) que habían considerado hacer esto, en realidad, proporcionando una versión genérica de 'IIf'.Me alegra que siguieran la ruta que tomaron, ya que la versión genérica solucionaría el problema del boxeo pero aún no proporcionaría un cortocircuito (o de lo contrario sería bastante confuso tener una característica del lenguaje intrínseco en cortocircuito disfrazado como un método genérico).) –

11

Joel me adelantó una respuesta, pero aquí es un programa de ejemplo y el IL generado que demuestra que If() pasa al operador ternario subyacente de IL sin el boxeo.

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 

Como puede ver, IL no tiene ninguna declaración de 'recuadro'.

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

Dado el mismo programa pero utilizando la función IIf() anterior, se produce la siguiente IL. Puede ver tanto el boxeo como la llamada de función:

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

Awesome answer. ¡Gracias! –

Cuestiones relacionadas