Est-ce que le VB.NET « Si » l'opérateur cause de la boxe?
-
18-09-2019 - |
Question
Ceux d'entre nous qui ont travaillé dans VB / VB.NET ont vu un code similaire à cette abomination:
Dim name As String = IIf(obj Is Nothing, "", obj.Name)
Je dis "abomination" pour trois raisons simples:
-
IIf
est une fonction , dont tous les paramètres sont évalués; Par conséquent, siobj
est rien dans l'appel ci-dessus unNullReferenceException
sera jeté. Ce comportement est inattendu pour quelqu'un qui est habitué aux opérateurs ternaires court-circuité dans des langues comme C #. - Comme
IIf
est une fonction, il engage donc la surcharge d'un appel de fonction. Encore une fois, bien que ce n'est pas une grosse affaire, ça ne se sent pas bien à quelqu'un qui attend pour se comporter comme une opération ternaire intrinsèque à la langue. -
IIf
est non générique et accepte donc les paramètres de typeObject
, ce qui signifie que les boîtes d'appel suivantes (je crois) un total de trois entiers:' boxes 2nd and 3rd arguments as well as return value '
Dim value As Integer = IIf(condition, 1, -1)
Maintenant, dans une version plus récente de VB.NET (je ne suis pas sûr de ce que le nombre est), l'opérateur de If
a été introduit, qui fonctionne exactement de la même manière que la fonction IIf
mais (si je comprends bien) sans les mêmes défauts. C'est-à-dire, il fait fournir un court-circuit et est une opération intrinstic VB. Cependant, je ne suis pas sûr de la dernière partie. documentation MSDN ne semble pas indiquer si les boîtes de If
ses arguments ou ne pas. Est-ce que quelqu'un sait?
La solution
La chose principale est que vous avez identifié correctement la nouvelle If
comme opérateur plutôt qu'une fonction. Il est également typesafe et n'a donc pas besoin de la boxe, et est une application directe au conditionnel / ternaire /? opérateur en C / C ++ / C # / Java / etc
Même sans le nouvel opérateur, vous pouvez obtenir une certaine amélioration dans VB.Net avec ce 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
Autres conseils
Joel m'a battu à une réponse, mais voici un exemple de programme et généré IL qui démontre que si () passe à travers l'opérateur ternaire sous-jacente de l'IL sans la boxe.
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
Comme vous pouvez le voir l'IL n'a pas de déclaration « boîte ».
.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
}
Étant donné le même programme, mais en utilisant la fonction IIf () plus, l'IL est produit suivant. Vous pouvez voir à la fois la boxe, et l'appel de fonction au-dessus:
.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
}