ViusalBasic程序員的.NET泛型編程

字號(hào):

一、引言
    在《The C++ Programming Language》一書(shū)中,Bjarne Stroustrop討論了模板方法在C語(yǔ)言中的偽實(shí)現(xiàn)-通過(guò)使用預(yù)處理和宏來(lái)模擬。Stroustrop創(chuàng)建了這種靈活運(yùn)用C語(yǔ)言的模板和宏的能力,它使得模板成為用C語(yǔ)言創(chuàng)建類(現(xiàn)在的C++)的相當(dāng)成熟的一部分。另外的優(yōu)點(diǎn)是,模板是由編譯器進(jìn)行類型檢查的,而不是簡(jiǎn)單地通過(guò)預(yù)處理器進(jìn)行文本替換。
    .net 2.0支持稱為泛型的模板。其基本概念是相同的:用一個(gè)或多個(gè)方法來(lái)定義一個(gè)方法或類,并指定數(shù)據(jù)類型作為一個(gè)可替代的元素。習(xí)慣情況下,都使用大寫(xiě)字符T。最后,當(dāng)你使用該方法或類時(shí),指明一種類型給泛型T,然后編譯器將基于那種類型使用新的信息來(lái)生成一個(gè)新的且的方法或類。這種新的元素變成了一個(gè)完整的獨(dú)特的代碼塊,這完全得益于編譯器的處理能力。
    本文將分析隨著.NET 2.0一起發(fā)布的泛型方法和類的定義以及泛型類的使用問(wèn)題。
    二、定義泛型方法
    .NET 2.0的泛型化能力可以定義到單一方法這樣的粒度。解決的方法是把數(shù)據(jù)類型與算法獨(dú)立開(kāi)來(lái)并參數(shù)化該數(shù)據(jù)類型。每一次用不同的數(shù)據(jù)類型對(duì)該方法的參照引用都產(chǎn)生一個(gè)不同的方法。泛型方法還支持約束和重載。一個(gè)泛型方法約束是一個(gè)新出現(xiàn)的語(yǔ)言特征,它可以把一個(gè)類型約束添加到泛型類型上去;這樣以來(lái)就限制了泛型參數(shù)的數(shù)據(jù)類型。因而,可以根據(jù)泛型參數(shù)的存在情況,對(duì)方法進(jìn)行重載。(接下來(lái)的幾個(gè)例子將分析重載的泛型和非泛型方法。)
    泛型的典型用法是把數(shù)據(jù)類型與通用算法分離開(kāi)來(lái),一個(gè)正規(guī)的例子就是把排序算法轉(zhuǎn)化成泛型排序算法。一個(gè)非常直接的例子是參數(shù)化一個(gè)Swap方法。列表1向你展示怎樣定義一個(gè)泛型Swap方法-該方法交換兩種任何類型的參數(shù)。它包含一個(gè)表明怎樣調(diào)用泛型方法的控制臺(tái)應(yīng)用程序:
    列表1:怎樣調(diào)用泛型方法
    Module Module1
    Sub Main()
    Dim I As Integer = 5
    Dim J As Integer = 7
    Swap(Of Integer)(I, J)
    Console.WriteLine("I = " & I)
    Console.WriteLine("J = " & J)
    Dim S As String = "Paul"
    Dim R As String = "Lori"
    Swap(Of String)(S, R)
    Console.WriteLine("S = " & S)
    Console.WriteLine("R = " & R)
    Console.ReadLine()
    End Sub
    Public Sub Swap(Of T)(ByRef a As T, ByRef b As T)
    Dim temp As T
    temp = a
    a = b
    b = temp
    End Sub
    End Module
    注意:在這個(gè)例子中,你也能把一個(gè)對(duì)象數(shù)據(jù)類型使用于該Swap方法,因?yàn)樗械?NET類型共享一個(gè)通用的類型。
    需要定義一個(gè)泛型方法的新的元素有圓括號(hào),Of關(guān)鍵字以及一個(gè)描述泛型類型的參數(shù),在方法名Swap的后面的字符(Of T)正是如此。之后,每當(dāng)使用該泛型類型,都要使用參數(shù)化參數(shù)(在本例中是T)。
    Swap是一個(gè)泛型方法,它有兩個(gè)可替換的參數(shù)和一個(gè)可替換的局部變量。例如,Swap(Of Integer)有效地實(shí)現(xiàn)了一個(gè)有兩個(gè)整數(shù)參數(shù)和一個(gè)定義為整數(shù)的臨時(shí)變量的Swap方法。
    1. 添加一個(gè)泛型方法約束
    假定你想約束列表1中Swap方法中的類型成為非null的結(jié)構(gòu)類型。你可以添加一個(gè)約束到Swap上去以指明Swap只可用于值類型(結(jié)構(gòu))。列表2顯示的Swap方法被約束到結(jié)構(gòu)(或值類型)上。最終結(jié)果,在列表1中的Swap(Of String)無(wú)法運(yùn)行:
    列表2:帶有約束的Swap方法把Swap方法限制到值類型:
    Public Sub Swap(Of T As Structure)(ByRef a As T, ByRef b As T)
    Dim temp As T
    temp = a
    a = b
    b = temp
    End Sub
    參數(shù)化類型可以被限制到結(jié)構(gòu)、類、基類、接口以及有缺省構(gòu)造器(如沒(méi)有參數(shù)的Sub New)的類型。列表2中的加粗的As謂詞展示了怎樣約束參數(shù)化的類型。
    泛型支持定義多個(gè)參數(shù)化類型,而每個(gè)參數(shù)化類型可以沒(méi)有或者有多個(gè)約束。
    2. 重載泛型方法
    你可以基于一些參數(shù)對(duì)方法進(jìn)行重載,但是沒(méi)有返回類型,而且方法也能夠被通過(guò)參數(shù)化類型所重載。例如,下面所有這些形式都可以存在于同一個(gè)范圍之內(nèi):
    Sub Foo
    Sub Foo(ByVal s As String)
    Sub Foo(Of T)( ByVal arg As T)
    Sub Foo(Of T, U)(ByVal arg1 As T, ByVal arg2 as U)
    三、定義一個(gè)泛型類
    泛型類與普通類一樣定義在相同的實(shí)例中,只有一個(gè)區(qū)別。當(dāng)你有一些數(shù)據(jù)和多于一個(gè)方法并且其間有關(guān)密切的關(guān)系時(shí),你可以定義一個(gè)普通類。當(dāng)方法和數(shù)據(jù)工作在一個(gè)密切的單元中并且該數(shù)據(jù)可以被進(jìn)一步抽象,以至相同的代碼可以支持許多數(shù)據(jù)類型時(shí),你定義一個(gè)泛型類。例如,隊(duì)列、列表和堆棧并不在乎它們存儲(chǔ)的東西,而僅在乎怎樣存儲(chǔ)這些東西。如果你使用一個(gè)其中存儲(chǔ)有對(duì)象的隊(duì)列、堆?;蛄斜恚敲茨悴坏貌辉谀愕拇a的很多地方進(jìn)行繁瑣的類型轉(zhuǎn)化。如果你使用一個(gè)泛型隊(duì)列、堆?;蛄斜?那么類型轉(zhuǎn)化只是發(fā)生在該類內(nèi)部。這就是說(shuō),繁瑣的類型轉(zhuǎn)化場(chǎng)所集中于該類的一個(gè)內(nèi)部點(diǎn)上,而該類的用戶可以依賴編譯器來(lái)進(jìn)行類型檢查,并不要求用戶執(zhí)行if條件檢查和類型變換。
    定義一個(gè)泛型類就象定義多個(gè)泛型方法,只是多了一點(diǎn):(Of T)構(gòu)造也可以用于類的頭部。為了說(shuō)明問(wèn)題,假定你已定義了一個(gè)派生于System.Collections.CollectionBase的泛型的強(qiáng)類型集合(見(jiàn)列表3)。那么現(xiàn)在,你可以把這個(gè)類使用于任何數(shù)據(jù)類型,就好象你已針對(duì)所有類型定義了一個(gè)定制的類型化的集合:
    列表3:一個(gè)泛型的強(qiáng)類型集合
    Module Module1
    Sub Main()
    Dim BrokenBones As TypedCollection(Of OrthoInjury) = New TypedCollection(Of OrthoInjury)
    BrokenBones.Add(New OrthoInjury(True,"Broken Right Clavicle", "Vicodin; Heals n 8 to 12 weeks"))
    BrokenBones.Add(New OrthoInjury(True, "Fractured Posterior Rib #5", "Heals in 6 to 8 weeks"))
    BrokenBones.Add(New OrthoInjury(True, "Fractured Posterior Rib #1", "Heals in 6 to 8 weeks"))
    Dim injury As OrthoInjury
    For Each injury In BrokenBones
    Console.WriteLine("Description: " & injury.Description)
    Next
    Console.ReadLine()
    End Sub
    End Module
    Public Class TypedCollection(Of T)
    Inherits System.Collections.CollectionBase
    Default Public Property Item(ByVal Index As Integer) As T
    Get
    Return CType(List(Index), T)
    End Get
    Set(ByVal value As T)
    List(Index) = value
    End Set
    End Property
    Public Function Add(ByVal value As T) As Integer
    Return List.Add(value)
    End Function
    End Class
    Public Class OrthoInjury
    Private FHasXray As Boolean
    Private FDescription As String
    Private FPrognosis As String
    Public Sub New(ByVal HasXray As Boolean, ByVal Description As String, ByVal Prognosis As String)
    FHasXray = HasXray
    FDescription = Description
    FPrognosis = Prognosis
    End Sub
    Public Property HasXray() As Boolean
    Get
    Return FHasXray
    End Get
    Set(ByVal value As Boolean)
    FHasXray = value
    End Set
    End Property
    Public Property Description() As String
    Get
    Return FDescription
    End Get
    Set(ByVal value As String)
    FDescription = value
    End Set
    End Property
    Public Property Prognosis() As String
    Get
    Return FPrognosis
    End Get
    Set(ByVal value As String)
    FPrognosis = value
    End Set
    End Property
    End Class
    如果你讀過(guò)以前關(guān)于類型化的集合的文章,那么你會(huì)看到類型化的集合泛型(列表3中的粗體部分)基本上是一種數(shù)據(jù)類型被參數(shù)化的強(qiáng)類型的集合。
    四、使用預(yù)定義的泛型類
    幸好,你不需要從頭開(kāi)始定義泛型類。System.Collections.Generics命名空間已定義好了許多典型的數(shù)據(jù)結(jié)構(gòu)用作泛型,例如List,Queue和Stack。你僅需要簡(jiǎn)單地導(dǎo)入該命名空間并聲明一個(gè)你需要的類型的實(shí)例即可。例如,下列代碼充分地實(shí)現(xiàn)用.net 2.0泛型List類型化的集合來(lái)替換你的定制的類型化的集合:
    Dim BrokenBones As System.Collections.Generic.List(Of OrthoInjury) = _
    New System.Collections.Generic.List(Of OrthoInjury)
    作為一個(gè)一般規(guī)則,如果你想要存儲(chǔ)多于一個(gè)類型(異類類型),可以使用更舊的風(fēng)格類,如Queue和Stack。如果你只想使用一種類型(同類類型),可以使用在System.Collections.Generic命名空間中的新的泛型類。通常情況下,你應(yīng)使用新的泛型類。
    五、選擇所學(xué)
    能夠把傳統(tǒng)的復(fù)雜的如C++這樣的語(yǔ)言與傳統(tǒng)的簡(jiǎn)單的如VB這樣的語(yǔ)言隔離開(kāi)來(lái)的東西越來(lái)越少了。乍看起來(lái),這種事實(shí)有點(diǎn)令人心灰意冷,因?yàn)檫@就意味著如今的VB越發(fā)難學(xué)了-就象C++一樣。不過(guò)事實(shí)上,任何一種語(yǔ)言(包括VB)的核心往往是非常相似的;而即使象C++這樣的語(yǔ)言,你也總可以選擇學(xué)習(xí)如泛型這樣更高級(jí)的概念,如果你需要它們的話??傊?,你可以選擇任何你想學(xué)的東西。
    還應(yīng)記住,你總是可以選擇學(xué)習(xí)任何編程相關(guān)的高級(jí)概念-開(kāi)始作為一個(gè)使用者-使用其中的內(nèi)容,例如泛型List;然后作為一個(gè)創(chuàng)建者,學(xué)習(xí)怎樣創(chuàng)建你自己的東西。總而言之,想短時(shí)間內(nèi)學(xué)精每一件技術(shù)可能無(wú)法實(shí)現(xiàn),而且也完全沒(méi)有必要。