¿El VB.NET “Si” causa de boxeo operador?
-
18-09-2019 - |
Pregunta
Los que han trabajado en VB / VB.NET han visto un código similar a esta abominación:
Dim name As String = IIf(obj Is Nothing, "", obj.Name)
Yo digo "abominación" por tres razones simples:
-
IIf
es un función , cuyos parámetros son evaluados; por lo tanto, siobj
hay nada en la llamada anterior a continuación, unNullReferenceException
será lanzada. Este es un comportamiento inesperado para alguien que está acostumbrado a los operadores ternarios en cortocircuito en lenguajes como C #. - Debido
IIf
es una función, por tanto, incurre en la sobrecarga de una llamada de función. Una vez más, aunque esto no es un gran problema, simplemente no se siente bien para alguien que espera para que se comporte como una operación ternaria intrínseca al lenguaje. -
IIf
no es genérico y por lo tanto acepta parámetros de tipoObject
, lo que significa las siguientes cajas de llamada (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 alguna versión más reciente de VB.NET (no estoy seguro de lo que es el número), se introdujo el operador If
, que funciona exactamente de la misma manera que la función IIf
pero (como yo lo entiendo) y sin las mismas deficiencias. Es decir, que hace proporcionar cortocircuitos y es una operación VB intrinstic. Sin embargo, no estoy seguro acerca de la última parte. No parece que la MSDN la documentación para indicar si las cajas If
sus argumentos o no. ¿Alguien sabe?
Solución
Lo más importante es que ha identificado correctamente el nuevo If
como un operador en lugar de una función. También es typesafe y por lo tanto no necesita el boxeo, y es una asignación directa a la condicional / ternaria /? 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
Otros consejos
Joel me adelantó una respuesta, pero aquí es un ejemplo de programa y el IL generado que demuestra que Si () pasa a través de operador ternario subyacente de la 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 se puede ver la IL tiene ninguna declaración 'caja'.
.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 de más edad IIf (), el siguiente IL se produce. Se puede ver tanto en el boxeo, y la sobrecarga de 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
}