top
Loading...
向VisualBasic程序員介紹泛型
天極IT資訊短信服務 電腦小技巧
資費:包月5元
手機:
介紹:細處著手,巧處用功。高手和菜鳥之間的差別就是:高手什么都知道,菜鳥知道一些。電腦小技巧收集最新奇招高招,讓你輕松踏上高手之路。


本文用通俗的用語和大量的實例向Visual Basic程序員介紹了下一版Visual Basic.Net中將要增加的新功能——泛型。此文章可以幫助廣大VB用戶了解泛型,以便將來將泛型應用到自己的應用程序中。

應用:泛型

此應用展示了在Visual Basic.Net 中新增加的泛型功能。

新概念

在開始實現泛型以前,有必要花一點時間分析一下為什么要在Visual Basic.Net中增加這一功能。泛型技術源于需要用一種一般的方法處理對象各種可能的類型,而不需要關心他們具體的類型。比如在Visual Basic 6.0中,你能夠用一個Collection類儲存任何類型的對象。

Visual Basic 6.0 的集合

Visual Basic 6.0 確實允許你將任何東西儲存在一個Collection中。但是,Collection類有幾個限制。我們用一個例子來說明如何將這個Employee類儲存在集合中:

‘ Visual Basic 6.0 代碼:類模塊Employee
Public SSN As String
Public FirstName As String
Public LastName As String
Public Salary As Currency

將這個類儲在集合中的方法顯得非常直接。

‘ Visual Basic 6.0 代碼

Dim employees As New Collection
Dim emp As Employee
Set emp = New Employee
emp.SSN = "111-11-1111"
emp.FirstName = "Scott"
emp.LastName = "Swigart"
emp.Salary = 50000
employees.Add emp, emp.SSN

這段代碼首先創建了一個Collection的實例employees。接著Employee類創建了一個實例,并設置了一些數據。最后,Employee對象被添加到Collection,指定emp.SSN屬性作為關鍵字。下面的代碼展示了如何從Collection取出這個Employee對象的實例:

‘ Visual Basic 6.0 代碼
Dim emp2 As Employee
Set emp2 = employees("111-11-1111")

現在我們一起來研究一下Visual Basic 6.0的這種集合有什么限制。首先,你的初衷是讓employees這個集合只儲存Employee類型的對象。但是沒有任何機制可以防止將一個別的類型的對象放入這個employees集合,也沒有任何東西可以告訴你從這個集合中取出的數據是什么類型。下面的代碼照樣可以正確編譯:

Dim s As String
s = employees("111-11-1111")

雖然開發者可以很明確地知道這不能正確工作,但沒有辦法讓編譯器發現這個問題。這樣會發生一個運行時錯誤。集合的使用同樣限制了智能感知技術的發揮。看看下面這段代碼:

employees("111-11-1111").LastName = "SomeoneElse"

這說明你能直接編輯集合中的項目。但是,IDE的智能感知不能幫助你選擇LastName屬性。再一次重申,以前的Visual Basic 中集合可以存放任何東西。

使用集合的兩個最大的限制是性能和靈活性方面的損失。集合雖然容易使用,但作為一個動態數組使用時性能非常差。集合的設計使它更像是一個字典,所以當你需要的數據結構類似堆棧或隊列時,它也不是一個很好的選擇。

框架中的集合

.Net框架1.0/1.1通過增加集合的種類解決了一部分問題。新引入System.Collections命名空間以后,你就可以創建更多類型的集合,比如數組表、位數組、哈希表、隊列、排序表和堆棧。下表列出了這些類型的用法:

集合名稱用途
ArrayList數組表能夠創建動態增大的數組。
BitArray位數組是經過優化的,專用于儲存布爾值(真/假)的數組。
HashTable哈希表和Visual Basic 6.0中的Collection類非常相似。它允許你通過關鍵字找得到對應的值。不過關鍵字和值 還是任意類型的。
SortedList排序表和哈希表非常類似,唯一不同的是它的關鍵字總是排序的。這意味著當你用For …Each語法遍歷整個集合時,得到的項目總是經過排序的。
Queue隊列是一種能讓被儲存的對象先進先出的集合。
Stack堆棧是一種能讓被儲存的對象后進先出的集合。

.Net 框架1.0/1.1已經解決了Visual Basic 6.0中的集合在靈活性方面的限制,但是,這些集合仍然是弱類型的,因此你還是可以將任何東西存放到一個ArrayList中,雖然在一個指定的應用中,只有儲存唯一的類型才有意義。

你真正想要是指定每個關鍵字必須都是String而且每個值都是Employee類型。在.Net框架1.0和1.1種,你只有創建自己的類,當然這種方法有些繁瑣。如果使用新的.Net框架1.2,這個問題可以用很少的代碼來解決,方法就是使用泛型。

深入代碼

泛型能夠提供嚴格的類型檢查,更好的智能感知功能和更好的性能,就如上一節中介紹的一樣。換句話說,他們超越了以前所有集合類所能提供的優點。

感受泛型

接下來你將看到,當你創建一個泛型集合的實例的時候,你需要提供一些信息以便集合類能夠被強類型化。這樣做有很多優點,包括在編譯階段更多的檢查以確保創建更安全和更可靠的代碼,更好的智能感知以及更好的性能。有必要提及.Net框架1.2中是在以前版本的基礎上新增加泛型集合。.Net框架1.2不會強迫你使用泛型。

如果你想使用泛型類型,首先需要包含System.Collections.Generic這個命名空間。這允許訪問帶有泛型功能的Dictionary、List、Queue、SortedDictionary和Stack類。下面btnConsumeGenerics_Click事件中的代碼提供了一個使用泛型字典的例子:

‘ Visual Basic .NET 8.0 代碼

Private Sub btnConsumeGenerics_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConsumeGenerics.Click

Dim employees As New Dictionary(Of String, Employee)
Dim emp As Employee
emp = New Employee
emp.SSN = "111-11-1111"
emp.FirstName = "Scott"
emp.LastName = "Swigart"
emp.Salary = 50000
employees.Add(emp.SSN, emp)

Dim emp2 As Employee

emp2 = employees.Item("111-11-1111")
Dim s As String
's = employees.Item("111-11-1111") ' This is now a syntax error

employees.Item("111-11-1111").LastName = "SomeoneElse"

End Sub

深入查看這段代碼,你會注意到一些泛型技術中相當有趣的東西。首先泛型的類型是用這樣的方法具體化的:

Dim employees As New Dictionary(Of String, Employee)

這可以翻譯成“創建一個Dictionary,它的關鍵字是String類型,值是Employee類型”。任何時候試圖儲存一個不是Employee類型的對象都將導致編譯錯誤。有必要重申,如果使用了泛型,你再用錯類型得到的將是編譯錯誤而不是運行時錯誤。事實上下面這段代碼除非被注釋掉,否則不會通過編譯,就如同編譯器知道Dictionary是專用于儲存Employee對象,而不是String:

's = employees.Item("111-11-1111") ' This is now a syntax error

更進一步,你現在能獲得全面的能感知支持。如果你輸入“employees.Item(“111-11-1111”).”,將自動彈出Employee類型的成員,這說明Visual Studio知道Dictionary現在是專門儲存Employee類的集合。

正如你所見,泛型使用起來很簡單。強類型化的代碼可以避免運行時錯誤;智能感知會工作得更好。雖然使用泛型已經有非常充分的理由,不過使用泛型還有更多的優點:性能和代碼重用。

將泛型技術引入.Net框架的一個主要原因是為了提高性能。比如集合類可以比以前工作得更快,因為編譯器能夠針對集合所儲存的類型進行優化。下面的代碼比較了數組、ArrayList以及泛型List的性能:

txtOutput.Text = "Performance" & vbCrLf
Const iterations As Integer = 5000000
PerfTime.Start()
Dim myArray(iterations) As Integer
For i As Integer = 0 To iterations - 1
myArray(i) = i
Next
Dim elapsed As Integer = PerfTime.Stop
txtOutput.Text &= "Array time: " & elapsed & vbCrLf
myArray = Nothing
GC.Collect()
PerfTime.Start()
Dim myArrayList As New ArrayList
For i As Integer = 0 To iterations - 1
myArrayList.Add(i)
Next
elapsed = PerfTime.Stop
txtOutput.Text &= "ArrayList time: " & elapsed & vbCrLf
myArrayList = Nothing
GC.Collect()
PerfTime.Start()
Dim myList As New List(Of Integer)
For i As Integer = 0 To iterations - 1
myList.Add(i)
Next
elapsed = PerfTime.Stop
txtOutput.Text &= "List time: " & elapsed & vbCrLf
myList = Nothing
GC.Collect()

這段代碼在固定長度的數組中儲存了500萬個數值,同時也在自動增長的ArrayList和泛型List中儲存同樣多的數值,性能數值看起來非常有趣:

Array 時間: 344

ArrayList時間: 4656

List時間: 797

有特定類型的定長數組有無與倫比的速度,而且不需要為改變大小付出代價。而集合類型的大小都是自動增長,如果有固定數組1/2的性能是相當不錯的。接下來看看ArrayList,非常不幸,只有固定數據1/10的性能。問題出在ArrayList被設計成儲存引用型變量,Integer是值類型,在儲存到ArrayList以前要經過“裝箱”操作,將Integer轉為Object型。裝箱的代價是非常昂貴的,所以當你儲存值類型數據(如Integer、Date、Boolean以及你自己創建的Structure等)時,使用泛型將獲得非常可觀的性能提升。

更多關于“裝箱”和“拆箱”操作的信息,請參見MSDN庫中的“裝箱轉換”和“拆箱轉換”

創建泛型類型和方法

并不是只能使用Visual Basic.Net提供的泛型類型,你可以創建你自己的泛型類型和方法。

泛型方法

當你想實現一些不與特定類型相關的一般算法時,你可能想創建泛型方法。舉個例子,典型的冒泡排序需要遍歷數組中的所有項目,兩兩比較,并交換需要排序的數值。

如果你已經確定只要進行整數的排序,你可以簡單地編寫一個只能用于Integer類型的Swap方法。但是如果你想能夠排序任何類型,你就可以編寫一個泛型Swap方法如下:

Private Sub Swap(Of ItemType)(ByRef v1 As ItemType, ByRef v2 As ItemType)

Dim temp As ItemType
temp = v1
v1 = v2
v2 = temp

End Sub

注意“Of ItemType”,當Swap方法被調用時,除了必須提供所需的參數,還必須傳入一個數據類型。這個數據類型會代替任何實例中的ItemType。下面的例子調用了Swap:

Swap(Of Integer)(v1, v2)

這條語句告訴Swap方法它將交換的是Integer類型。如果你回過頭去看看Swap的代碼,這條語句的意思就是讓JIT將所有的ItemType換成Integer,這個Swap方法實際上已經被JIT重寫成:

Private Sub Swap(ByRef v1 As Integer, ByRef v2 As Integer)
Dim temp As Integer
temp = v1
v1 = v2
v2 = temp
End Sub

這是實際執行的代碼,JIT生成一個專用于Integer類型的方法。如果你接下來想要排序字符串類型,你就可以用另一Swap的調用如下:

Swap(Of String)(v1, v2)

當方法執行的時候,JIT會生成另一個版本的Swap,這次是特定成String類型的:

Private Sub Swap(ByRef v1 As String, ByRef v2 As String)
Dim temp As String
temp = v1
v1 = v2
v2 = temp
End Sub

下面是一個使用泛型Swap的冒泡排序的完整例子:

Private Sub btnSortIntegers_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSortIntegers.Click

Dim ints(9) As Integer
Dim r As New Random
For i As Integer = 0 To 9
ints(i) = r.Next(1, 100)
Next

' 冒泡排序

For j As Integer = 0 To 9
For k As Integer = 9 To 1 Step -1
If ints(k) < ints(k - 1) Then
Swap(Of Integer)(ints(k), ints(k - 1))
End If
Next
Next

txtOutput.Text = "Sort Integers" & vbCrLf
For i As Integer = 0 To 9
txtOutput.Text &= ints(i) & vbCrLf
Next

End Sub

泛型類型

最后一點,你能夠創建完全泛型的類型,使用這種“Of ItemType”方法創建類的聲明如下:

Public Class SomeClass(Of ItemType)

Private internalVar as ItemType
Public Function SomeMethod(ByVal value As ItemType) As ItemType
End Function

End Class

這段代碼對類的作用與方法是相同的。JIT編譯器會簡單地將實例中的ItemType替換成實例化時特別指明的類型。

約束

泛型技術還支持一種叫做約束的特性。這項功能確保在指定類型的時候,傳入的類型最起碼要實現某些功能。比如你要實現一種排序算法,你需要確保傳入的類型能夠實現IComparible接口。你可以用約束來完成這個設想:

Public Class SomeClass(Of ItemType As IComparible)

Public Function SomeMethod(ByVal value As ItemType) As ItemType

End Function

End Class

結論

泛型技術相對于以Object為基礎的集合提供了很多好處,首先,泛型類是強類型的,這就確保所有的錯誤在編譯時能夠發現。強類型還可以讓智能感知提供更多方便。泛型還能讓你簡化代碼,讓你的算法可以作用于多種類型。最后,泛型集合要比以Object為基礎的集合快得多,特別是用于值類型時。

作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗