Вызывает ли оператор VB.NET “If” блокировку?
-
18-09-2019 - |
Вопрос
Те из нас, кто работал в VB / VB.NET, видели код, похожий на эту мерзость:
Dim name As String = IIf(obj Is Nothing, "", obj.Name)
Я говорю "мерзость" по трем простым причинам:
IIf
является функция, все параметры которого оцениваются;следовательно, еслиobj
ничего ли в приведенном выше вызове, тогда aNullReferenceException
будет выброшен.Это неожиданное поведение для тех, кто привык к короткозамкнутым троичным операторам в таких языках, как C #.- Потому что
IIf
является функцией, таким образом, она несет накладные расходы на вызов функции.Опять же, хотя в этом нет ничего особенного, это просто кажется неправильным тому, кто ожидает, что это будет вести себя как троичная операция, присущая языку. 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
приводит свои аргументы или нет.Кто-нибудь знает?
Решение
Главное, чтобы вы правильно определили новый 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
Другие советы
Джоэл опередил меня с ответом, но вот пример программы и сгенерированный 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
}