1. 程式人生 > >淺談VB6中的自定義類的使用

淺談VB6中的自定義類的使用

   PS:除非特別宣告,本文所說VB指的是VB6,而非VB.NET。

   大家都知道,VB是一種半面向物件(也有人稱之為“偽面向物件”)的語言,他雖然可以寫自定義的類,但是由於種種原因,使得他在這方面的發育產生了一點問題,比如說:VB寫出來的類是不能繼承的(不孕不育?!-_-#...傳說VB的偶像是東方不敗……殘念-_-#!),你無法像在C++/C#/JAVA==中一樣自由的使用類。你很難想象類沒有繼承會有啥用途。還有,在VB中,一個類模組只能寫一個類,這意味著如果你的Project中使用了N個類,你必須忍受它帶著N個類模組檔案,甚至其中有些只是小小小類。這些都造成VB的類的可用性和重用性不佳。
   但是,VB是否在類的方面就這麼無能呢?答案是否定的,在很多時候(特別是需要將很多功能封裝,形成模組化的時候),VB 的自定義類還是能派上用場的。這裡,有人可能會想到使用ActiveX控制元件,雖然在VB中,可以使用ActiveX控制元件做到上述功能,但是寫ActiveX控制元件要比寫類麻煩得多,而且ActiveX控制元件的資源佔用也要比類大。當然,如果你只想去往上搜搜ActiveX控制元件,然後拼成一個程式
的話,我完全可以對你說:“原諒我浪費了你的生命!”

   那麼接下來,我們就已Animal為主題講講在VB6中寫自定義類的方法~

  成員函式和成員變數

   首先,我們需要新增一個類模組,然後將它的名稱改成Animal,那麼,這個類的名字就是Animal。然後再寫入下列程式碼:
引用:
Private m_strName As String, m_bytLegs As Byte, m_blnBark As Boolean

Public Sub 
SetAnimalName(ByVal strName As String)

   m_strName = strName  
'/// 設定Animal的名字

End Sub

Public Function 
GetAnimalName() As String

  
GetAnimalName = m_strName  
'/// 返回Animal的名字

End Function

Public Sub 
SetLegs(ByVal bytLegs As Byte)

   m_bytLegs = bytLegs  
'/// 設定Animal的腿數

End Sub

Public Function 
GetLegs() As Byte

  
GetLegs = m_bytLegs  
'/// 返回Animal的腿數

End Function

Public Sub 
SetBark(ByVal 
blnBark As Boolean)

   m_blnBark = blnBark  
'/// 設定Animal是否會叫

End Sub

Public Function 
GetBark() As Boolean

  
GetBark = m_blnBark  
'/// 返回Animal是否會叫

End Function
如此,我們已經寫好了Animal類的大體結構,現在我們來看看這些程式碼。
引用:
Private m_strName As String, m_bytLegs As Byte, m_blnBark As Boolea  '/// 宣告成員變數
這裡的Private等同於Dim,為了更直觀,所以這樣寫。因為在類模組中,也可以用Public宣告變數。用Private(Dim)和Public宣告的變數,他們的作用域不同:Private宣告的變數只能在類的內部被訪問,在外部是不可見的,而用Public宣告的變數在外部是可見的,這意味著你在外部就可以直接修改類中的變數,這看起來很方便,但是任何無意的修改所造成的後果都可能是無法預估的。所以,為了更好的封裝類,通常用Private宣告。當然,在某些特定條件下,我們也可以使用Public。(詳情請看下文)

   然後,我們定義了幾個成員函式,用它完成相應的功能。至於什麼是成員函式(還有剛才提到的成員變數),這點大家可以不必瞭解,這其實是C++的叫法,只不過上校在VB中也習慣這麼叫。這完全是稱呼問題。大家可以看到,我把變數名寫成m_XXX,這裡的m_是成員變數的標記,用於和其他變數區分,這是典型的匈牙利命名法。

   BTW:關於匈牙利命名法,大家可以去Google瞭解下。個人感覺匈牙利命名法在VB中實用性很強,所以我的宣告習慣基本都符合匈牙利命名法。

   然後我們看看後面的一些函式。他們都用Public宣告,原因和前面宣告成員變數類似,這裡不再闡述。函式功能也都已註釋,大家可以自己看。

類的使用方法

   類寫好了,我們要開始使用它。和使用其他資料型別一樣,我們要先宣告。但是有所不同的是,類的宣告通常有兩種方法:
引用:
Dim cAnimal As Animal

Set cAnimal = New Animal
————————————————————
Dim cAnimal As New Animal
兩者相同之處在於都聲明瞭一個名為cAnimal的物件變數,並用New將其例項化,這是使用自定義類所必需的。而兩者的不同在於後者在宣告Animal物件變數的時候就將其例項化,而前者則是先宣告物件變數,再在適當的時候將其例項化。相比之下,前者可以控制例項化類的時間和場合,因此更為自由,普遍。

說完了宣告我們先來講講類的銷燬。每一個類的使用都要佔用一定的資源,當不需要這些類時,需要釋放他們所佔用的資源,這是良好的Coding Habits~
   類的銷燬比較簡單,只需要
引用:
Set cAnimal = Nothing
即可。
這裡建議大家在宣告完類之後,就馬上在適當的地方寫入銷燬類的程式碼,避免因為遺忘而造成錯誤。

接下來,我們要開始使用我們的Animal類
引用:
Private Sub Form_Load()

  
'/// 設定相關選項
  
  
Set cAnimal = New Animal
  
  
With cAnimal
  
.SetAnimalName (
"IceBear")

.SetLegs (
4)

.SetBark (
True)
  
  
End With

End Sub
我們在窗體載入的時候例項化了cAnimal這個類變數,,然後設定了Name/Legs/Bark等屬性。
使用類例項和使用控制元件很像,當輸入類名+. 即cAnimal.的時候,就會出現資訊條

然後進行相應操作即可。
引用:
Private Sub Form_Click()

  
With cAnimal
  
Debug.Print 
"Animal Name:" & .GetAnimalName & vbNewLine & "Animal Legs:" & .GetLegs

Debug.Print 
"Is Bark:" & .GetBark
  
  
End With

End Sub
單擊視窗後,立即視窗就會出現:
引用:
Animal Name:IceBear
Animal Legs:4
Is Bark:True
這時,你就可以向你暗戀的女生說你能在VB裡寫類了。(但是千萬別提到這類不能繼承……-_-#!)

類的屬性

不知道大家有沒有發現,無論我們是獲取,還是設定Name/Legs/Bark的資訊,都需要使用一個單獨的成員函式。而我們在使用控制元件時,那些都是作為屬性(Property)來實現的。所以,接下來,我們要研究如何在自定義類中實現屬性。

為了方便之後對比,我們僅把Name轉化為屬性實現:
引用:
Public Property Get Name() As String

  
Name = m_strName  
'/// 返回Animal的名字

End Property

Public Property Let 
Name(ByVal strAnimalName As String)

   m_strName = strAnimalName  
'/// 設定Animal的名字

End Property

我們可以發現,屬性的定義和成員函式定義很相似。Get就像Function,而Let類似於Sub。因為Get是返回屬性,所以通常沒有引數,而Let為了讓我們設定屬性,所以必須要有一個引數。
這樣,我們就可以像在控制元件中一樣,直接使用Name這個屬性來設定和獲取相關資訊。

BTW:如果你覺得編寫屬性過程太麻煩,可以使用工具選單中的新增過程來減輕工作量
   另外,我們可以通過只編寫Let或者Get過程來生成只讀/只寫的屬性

看到這裡,不知道大家想過沒有,既然Get/Let屬性過程很像Function/Sub,而Function/Sub是可以有多個引數的,那麼Get/Let屬性是不是也可以有引數呢?
當然,這是可以的,只不過相當少見(我們平常在用控制元件屬性時,MS都沒遇到過),而且它還有著比較嚴格的規定和限制,如果你對這方面不感興趣或者認為用不著,可以跳到後面去。
現在我們就額外新增一個Age的引數,看看如何在屬性過程中使用多引數。
首先我們的屬性過程要改為:
引用:
Public Property Get Name(ByVal intAge As IntegerAs String

  
Name = m_strName  
'/// 返回Animal的名字

End Property

Public Property Let 
Name(ByVal intAge As IntegerByVal strAnimalName As String)

   m_strName = strAnimalName & 
" is " & intAge

End Property
Get和Let同時多了一個相同的引數。不管怎麼樣,Let過程總會比Get多一個引數。並且,Get過程的1--N個引數要和Let過程的1--N個引數一致(包括函式名、型別、傳入方式等),Get的返回值型別要和Let的第N+1 個引數(最後一個引數)的型別相等。
我們可以通過一張圖片來大致瞭解下Let過程的呼叫。

所以,相應的,我們對Name屬性的程式碼也要更改如下:
引用:
.Name(20) = "IceBear"
& .Name(19)
執行後,立即視窗就會出現:
引用:
Animal Name:IceBear is 20
Animal Legs:4
Is Bark:True
但是,當我們在使用Get過程時,那個引數並沒有作用,卻又必須存在。這時,我們可以將其宣告為可選引數,這樣,Get確有引數存在,我們也可以免去放入無用實參的麻煩。
引用:
Public Property Get Name(Optional ByVal intAge As IntegerAs String
雖然在屬性過程中使用多個引數可以在一定程度上加快便捷,但是這是犧牲實用性為代價的。多個引數的過程屬性在使用上非常不便,而且看起來不倫不類的。所以,上校建議如非真的必要,就不要使用多引數。

特殊的類屬性

其實,在VB的Class裡,還有一種很普通但不普遍的類屬性——共有成員變數。
我們在之前強調過,一般情況下,成員變數應為私有,這是封裝的要求。而如果成員變數的屬性是共有的,則其在外部也可以被訪問,這就形成了一個簡單的類屬性,屬性名就是他的變數名。
和屬性過程相比,公有成員變數不能設定為只讀/只寫,無法在更改自己的同時更改其他的屬性值,也不能驗證資料等。(因為修改共有變數屬性僅是修改它的值,而通過屬性過程,則可以進行一系列的操作)但是共有成員變數作為屬性,能很大程度上減少程式碼量,簡化操作。下面有一些使用類屬性的原則,僅供參考:
引用:
From:M$ MSDN
以下情況應使用屬性過程: 
•        屬性為只讀,或一旦設定就不能改變。
•        屬性已設定的值需要驗證。
•        超出特定範圍的值。例如,負數,雖符合屬性的資料型別,但屬性如果允許這樣的假設值出現,就會導致程式出錯。
•        屬性的設定可導致一些物件狀態的明顯變化,例如,Visible 屬性。
•        屬性設定可改變內部變數或其它屬性的值。 
以下場合應使用只讀屬性的公共變數: 
•        屬性是自驗證型別。例如,如果一個不是 True 或 False 的值被賦給 Boolean 變數,則或者出錯,或者資料自動轉換。
•        在資料型別所支援的範圍內的值都是有效的。象許多 Single 或 Double 型別的屬性。
•        屬性是 String 資料型別,並沒有大小或字串取值的限制。
BTW: 不要僅僅為了避免函式呼叫的額外開銷而用公共變數來實現一個屬性。其實,由於型別庫的要求,Visual Basic 在類模組中以任意方式將公共變數作為屬性過程對使用。

屬性和成員函式的選擇

在類編寫中,經常會遇到選擇屬性還是成員函式實現的問題,就此,我們對屬性和成員函式的選擇給出自己的判斷標準(僅供參考):
一般來說,屬性是對某個物件本身特徵的描述,屬性是對向所擁有的,比如Name、Age、Height等等;而成員函式則用於執行物件的某種行為/動作,比如Move、Jump、Cook等。(此處是對於VB來說,CPP MS沒有類似於VB屬性過程的東西,所以成員函式也用於設定屬性。)
不過,有時,就像人類行為也存在一個邊界不清一樣,可能上述的方法不管用。那麼,你可以根據自身的要求和條件或者說癖好決定。
這裡,還有最後一招,這招是上校的獨門絕技,不僅在此處適用,而且在任何學科,任何情況下皆適用。最大的好處在於你會無條件的服從自己~這招就是~~鐺~~鐺~~鐺~~擲——硬——幣。
回聲:)讓我送你去見毛主席吧~-_-#!

建構函式與解構函式

當我們希望類在被初始化時,自動的執行一些操作。比如:為某個成員變數賦初始值。這時候,我們就需要建構函式。當然,這裡的建構函式(和下文的解構函式)同樣是CPP的叫法。
建構函式表示為:
引用:
Private Sub Class_Initialize()

End Sub
BTW:大家可以先在左上端的物件下拉框內選擇Class,然後在右邊選擇Initialize
比方說,我們希望在類被初始化,m_strName自動被賦予“IceBear”,只需:
引用:
Private Sub Class_Initialize()

   m_strName = 
"IceBear"

End Sub
說完建構函式,咱再說說解構函式。
解構函式是類被銷燬時要進行的操作,通常用於清理、釋放等操作。他的函式名是:
引用:
Private Sub Class_Terminate()
解構函式的用法和建構函式相似,這裡不再闡述。

缺憾

VB的類的缺陷是比較多的,就像我們剛開始瞭解的那樣,他不能夠繼承,而且用類模組其實是一件挺煩的事。相對於其他OOP語言來說,VB的類還不支援真正的多型(VB是通過多重 ActiveX 介面來提供多型的),而友元效果也不太令人滿意。
還有一點比較重要,就是在類模組中是不能使用子類化(SubClass)和鉤子(Hook)的。(除非你會用ASM寫一個處理子程,然後掛接)

尾聲

至此,我們已經瞭解了VB類的大體使用方法,大家如果對此感興趣的話,可以自己通過MSDN學習:事件(events)、友元(Friend:VB中是這樣)等更高階的技術。
咳...咳...上校順便插段AD:
http://bbs.cfan.com.cn/viewthrea ... p%3Bfilter%3Ddigest
這裡是上校自己寫的托盤圖示類,大家有興趣可以看看Source Code。