[.NET]VB.NET中轉換成數值方式比較

[.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的說明。

Boxing 和 Unboxing (C# 程式設計手冊)

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

 

Boxing and Unboxing in VB.NET

但如果把那個範例用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速度更是快呀~~

image

 

參考資料

Boxing 和 Unboxing (C# 程式設計手冊)

Boxing and Unboxing in VB.NET

String.Split 方法

Comparing String to Integer Conversion Methods in VB.NET

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^