2010-03-09 53 views
18

我們這些誰一直在VB/VB.NET曾見過類似這樣的憎惡代碼:是否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函數完全相同,但是(據我所知)沒有相同的shor tcomings。也就是說,它確實提供了短路,而它一個內在的VB操作。但是,我不確定最後一部分。 MSDN documentation似乎並不表示If是否包含其論點。有人知道嗎?

+0

+1這個問題很有趣! – 2010-03-09 15:32:48

回答

12

最主要的是你正確識別新的If作爲運營商而不是一個函數。它也是類型安全的,因此不需要裝箱,並且是直接映射到條件/三元/運營商在C/C++/C#/的Java /等

即使沒有新的運營商,你可以得到一些改善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 
+0

我注意到VB.NET團隊中的一些開發人員的博客(忘記是誰)他們曾考慮過這樣做,實際上 - 提供了一個通用版本的'IIf'。我很高興他們走了他們所做的路線,因爲通用版本可以解決拳擊問題,但是仍然不會提供短路(否則將僞裝爲通用方法的短路內在語言功能會相當混亂)。 – 2010-03-09 15:07:31

11

喬爾打我一個答案,但在這裏是一個示例程序和生成的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 
} 
+0

太棒了。謝謝! – 2010-03-09 15:43:24