[.NET]VB.NET中轉換成數值方式比較
最近有朋友提到Boxing & Unboxing的問題,用類似下面的Code來討論。
Dim aa As String = " 123 11 22 33 44 55"
Dim bb As Array
bb = Split(aa, " ")
Dim bb1 As String = bb(1) 'boxing
在討論前,先來看看Boxing & Unboxing的說明。
Box 和 Unbox 處理可讓您將實值型別視為物件處理。以 Box 處理實值型別時,會將型別封裝到 Object 參考型別的執行個體內。如此一來,便可將實值型別存放在記憶體回收堆積上。Unbox 處理則會從物件擷取實值型別。在這個範例中,整數變數 i 是以「Box」進行處理,然後指派給物件 。
int i = 123;
object o = (object)i; // boxing
o = 123;
i = (int)o; // unboxing
看一下iL,有做box & unbox,如下,
.maxstack 1
.locals init ([0] int32 i,
[1] object o)
IL_0000: nop
IL_0001: ldc.i4.s 123
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: box [mscorlib]System.Int32
IL_000a: stloc.1
IL_000b: ldc.i4.s 123
IL_000d: box [mscorlib]System.Int32
IL_0012: stloc.1
IL_0013: ldloc.1
IL_0014: unbox.any [mscorlib]System.Int32
IL_0019: stloc.0
IL_001a: ret
但如果把那個範例用VB.NET來Run一下,
Dim i As Integer = 123
Dim o As Object = i ' boxing
Dim j As Integer = CInt(o) ' unboxing
看一下iL,
.maxstack 1
.locals init ([0] int32 i,
[1] int32 j,
[2] object o)
IL_0000: nop
IL_0001: ldc.i4.s 123
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: box [mscorlib]System.Int32
IL_000a: stloc.2
IL_000b: ldloc.2
IL_000c: call int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToInteger(object)
IL_0011: stloc.1
IL_0012: nop
IL_0013: ret
在VB.NET中的CInt(o)會改用Conversions.ToInteger(o)來處理,所以不會看到 unbox.any ,而是看到 Conversions::ToInteger(object) 。
所以再回頭來看看原本要討論的Code,
在取出bb(1)時,會做boxing,因為要從bb字串Array中取出字串放到Object型態之中,從反組譯可得知
Dim bb1 As String = Conversions.ToString(NewLateBinding.LateIndexGet(Strings.Split(aa, " ", -1, CompareMethod.Binary), New Object() { 1 }, Nothing))
那要如何避免boxing呢? 就是避免將實值型別放到Object之中,所以將Dim bb As Array 改成 Dim bb() As String,就不會有box的情形了!
所以如果用10000000次一點來比較時間的話,boxing的確會多花很多時間,如下,
'Boxing implicit conversion
Dim t_s As Long = Now.Ticks
Dim aa As String = " 123 11 22 33 44 55"
Dim bb As Array = Split(aa, " ")
Dim cc As Integer = 123
For i = 1 To 10000000
If bb(1) = cc Then
End If
Next
Me.ListBox1.Items.Add("Boxing implicit conversion :" & Format((Now.Ticks - t_s) / 10000000, "0.0000000") & "s")
'約花33秒
'implicit conversion
Dim t_s As Long = Now.Ticks
Dim aa As String = " 123 11 22 33 44 55"
Dim bb() As String = Split(aa, " ")
Dim cc As Integer = 123
For i = 1 To 10000000
If bb(1) = cc Then
End If
Next
Me.ListBox1.Items.Add("implicit conversion :" & Format((Now.Ticks - t_s) / 10000000, "0.0000000") & "s")
'約花13.1秒
所以boxing的確花了蠻久的時間,而implicit conversion也是花了不少的時間。
所以如果我們自已來做convert呢? 是不是能更快呢? 答案是肯定的,以下使用Integer.Parse
'Integer.Parse
Dim t_s As Long = Now.Ticks
Dim aa As String = " 123 11 22 33 44 55"
Dim bb() As String = Split(aa, " ")
Dim cc As Integer = 123
For i = 1 To 10000000
If Integer.Parse(bb(1)) = cc Then
End If
Next
Me.ListBox1.Items.Add("Integer.Parse(bb(1)) :" & Format((Now.Ticks - t_s) / 10000000, "0.0000000") & "s")
'約花1.9秒
那是不是有更快的轉換呢? 在VB.NET中是有的,就是用Conversion.Val,速度更快。
Dim t_s As Long = Now.Ticks
Dim aa As String = " 123 11 22 33 44 55"
Dim bb() As String = Split(aa, " ")
Dim cc As Integer = 123
For i = 1 To 10000000
'Conversion.Val 方法 (Object) http://msdn.microsoft.com/zh-tw/library/microsoft.visualbasic.conversion.val(v=vs.80)
If Conversion.Val(bb(1)) = cc Then
End If
Next
Me.ListBox1.Items.Add("Conversion.Val(bb(1)):" & Format((Now.Ticks - t_s) / 10000000, "0.0000000") & "s")
'約花0.37秒
以上的測試是經過10000000次的執行所產生的結果,平常在寫程式時,除了注意Boxing/Unboxing的問題外,型別的轉換也是要一併考量。
使用CInt效能很慢,如果要字串要轉換數值的話,就使用Parse吧! 在VB.NET中還有個Val速度更是快呀~~
參考資料
Comparing String to Integer Conversion Methods in VB.NET
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^