Вызывает ли оператор VB.NET «If» бокс? - PullRequest
18 голосов
/ 09 марта 2010

Те из нас, кто работал в VB / VB.NET, видели код, похожий на эту мерзость:

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

Я говорю "мерзость" по трем простым причинам:

  1. IIf - это функция , все параметры которой оцениваются; следовательно, если obj - ничто в вышеупомянутом вызове, тогда NullReferenceException будет брошен. Это неожиданное поведение для тех, кто привык к короткозамкнутым троичным операторам в таких языках, как C #.
  2. Поскольку IIf является функцией, она, таким образом, несет накладные расходы при вызове функции. Опять же, хотя это не имеет большого значения, просто не кажется правильным, если кто-то ожидает, что он будет вести себя как троичная операция, свойственная языку.
  3. IIf не является универсальным и поэтому принимает параметры типа Object, что означает следующие поля вызова (я полагаю) всего три целых числа:

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

Теперь, в более поздней версии VB.NET (я не уверен, что это за число) был введен оператор If, который работает точно так же, как и функция IIf, но (как я понимаю это) без таких же недостатков. То есть, обеспечивает короткое замыкание, а является внутренней операцией VB. Однако я не уверен насчет последней части. Документация MSDN не указывает, содержит ли If свои аргументы или нет. Кто-нибудь знает?

Ответы [ 2 ]

12 голосов
/ 09 марта 2010

Главное, что вы правильно определили новый If как оператор , а не функцию.Он также безопасен для типов и, следовательно, не нуждается в боксе и является прямым сопоставлением с условным / троичным /?оператор в C / C ++ / C # / Java / etc

Даже без нового оператора вы можете получить некоторые улучшения в VB.Net с помощью этого кода:

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
11 голосов
/ 09 марта 2010

Джоэл добил меня до ответа, но вот пример программы и сгенерированного IL, который демонстрирует, что If () проходит через лежащий в основе троичный оператор IL без упаковки.

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

Как видите, у IL нет оператора 'box'.

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

С учетом той же программы, но с использованием более старой функции IIf (), получается следующий IL. Вы можете увидеть как бокс, так и служебные вызовы функций:

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