VBA 类模块理解和使用总结
目 錄
- VBA 類模塊理解和使用總結
- 一、類的概念
- 二、類的定義
- 三、類詳細定義
- 關于封裝
- 關于多態
- 關于異常
- 關于自定義事件
- 四、結論:
VBA 類模塊理解和使用總結
一、類的概念
記得有人總結,VBA是基于面向對象(OOP)的編程語言,而 java是完全面向對象的編程語言,為了更好地理解面向對象編程,去學習了一下 java基礎知識,現在回過來看VBA的面向對象特性就比較好理解,只是兩者語法相差甚遠。
剛開始時,如何理解“對象”這個概念也是個問題,在面向對象編程中使用的單詞是“object”,查字典發現其含義是 “物體;物品;東西;(極欲得到、研究、注意等的)對象;宗旨;目的;目標”,才知道對象就是物體、東西或研究的對象,我們中文翻譯成對象。
要理解對象,還必須要了解“類”這個概念。我們可以從生活中理解一下,如果我們到超市購買一些餅干,可能是圓形或星型等各種形狀,上面有許多好看的花紋或圖案,這在流水線上生產時肯定有相應的模具的才能生成這種餅干,這個模具就是類。
其實面向對象編程中的類就是對某種一個個具體對象進行一定抽象而形成的概念。例如,現實世界上有各種各樣的人——男人、女人、白人、有色人、大人、小孩等等,通過一定的抽象為人類,如果往更高一層抽象可以是動物。同樣的還有狗類,汽車類……,這些類的概念就是面向對象編程中的需要定義的類,然后可以利用該類創建具體的對象,其屬性值可以相同或不同。我們在抽象成一個類時,編程所關注的無非是對象的部分屬性和方法,例如學生類(Student),我們關注的屬性可能有:學號、姓名、出生日期、籍貫、班級等,還有很多屬性我們可能不需要關注而已,例如是否雙眼皮、頭上是否有兩個旋等。
在VBA中,已經定義了許許多多的類和對象供大家使用,例如Excel中的工作簿(workbook)、工作表(worksheet)、單元格(range或cell)、行(row)等等。一般我們可能不需要自定義類就可以實現所需的功能,但是如果理解類和對象,并加以運用,可能會更好。
二、類的定義
類的定義在不同的語言中是不同的,這里先給出java語言中類的定義:
java 類定義的示例:
public class Person {public string name; // 聲明字符串類型的變量name,用于保存姓名,是對象的姓名屬性private int age; // 聲明int類型的變量age,用于保存年齡,是對象的年齡屬性,int就是VBA中的Integerpublic void setAge(int age){ // 間接對屬性age賦值的方法,并且進行一些控制if(age <= 0 || age > 130){throw new RuntimeException("年齡范圍不合法");}this.age= age;}public int getAge(){ // 間接獲取age的值return this.age;}// 定義方法 speak() 說話public void speak() { System.out.println("我姓名是" + name + "今年" + age + "歲了!");} }class是關鍵字,其中文含義就是“類”,Person就是類名,業內約定首字母大寫以表示類,從而與其他名稱相區別。這個類中聲明了兩個屬性和一個方法。其中name屬性前面使用了修飾符public,其含義是公共的,外面可以直接訪問該成員,起不到封裝作用;而age屬性前面使用了 private(私有的)修飾符,這樣外面不能直接訪問,所以要定義 setter、getter方法,提供對該屬性進行賦值或獲取,也可以在方法內進行一些控制,起到了類的封裝作用;當然也可以只提供getter方法,就能實現只讀功能。
一般類具有三大特征:封裝、繼承和多態。
-
關于封裝,上面的示例主要體現了封裝特性,就好像我們買來的電視機,外面都是有材料包裹的,你看不到內部細節,但是會留出幾個接口供你使用,你可以使用遙控器或直接按壓按鈕操作接口。
-
關于繼承,可以這樣理解,你父親有些財產(屬性),也有一些秘訣(方法),你都能繼承(如果不是父類私有的),這時父親就是父類,你就是子類,子類繼承父類。在面向對象編程中,始終有一個公共的父類object,任何系統提供的類和自定義的類都會默認繼承這個object類,這點VBA中也是如此。
-
關于多態,子類繼承了父類,那么,在子類基層上創建的對象都可以賦值給父類,就好像父親可以代表子女處理他們的事件。具體代碼在此不表。
VBA中類的定義
在VBA中也有類似的概念,用面向對象編程的術語,類為基于它創建的所有對象定義了屬性(Properties),方法(methods)和事件(Events),其中的屬性、方法和事件統稱為類成員。到目前為止,我使用過vba中的類的封裝和多態,還沒有特別使用過vba的類的繼承,是可以繼承的!下面自定義類的兩個事件就是繼承而來的。
注意在VBA中要定義類比較特殊,首先需要插入一個類模塊,這個類模塊就是一個類,此時出現名稱為“類1”的模塊,我們把“類1”的名稱更改為 Student(就是類名),在右側的代碼區就可以聲明屬性,定義方法等。這樣就相當于java類的定義了。
例如: Excel中的“學生明細”表的數據格式(為了簡單,字段和記錄少點,實際工作上可能較多)如下:
這時,在VBE中可以插入一個類模塊,并把名稱改為 Student:
???????? ?? ??
在右側代碼區輸入:
' 類模塊代碼區 Public id As Integer ' 唯一代表學生的學號,因為姓名可能會重名, 聲明 id 為整數 Public name As String ' 姓名,聲明name 為字符串,public代表公共的,表示創建的對象這個屬性可以直接訪問和修改 Private mBirthday As Date ' 出生日期,聲明為日期, m表示me,由于沒有this.birthday這種形式,只能在變量名上做文章Public Property Let birthday(aBirthday As Date) ' aBirthday中第一字符a表示為參數argumentIf aBirthday < DateAdd("yyyy", -130, Date) Or aBirthday >= Date Then ThenErr.Raise 5555, "Student Class module", "年齡范圍不合法" ' 如果不符合要求,拋出一個自定義異常ElsemBirthday = aBirthdayEnd If End PropertyPublic Property Get birthday() As Date ' 屬性的getter方法,Property 中文含義是 “屬性”birthday = mBirthday End PropertyPublic Property Get age() As Integer ' 屬性的getter方法(這里沒有使用set ,如果要賦值的是對象的,把Get換成Set)age = DateDiff("yyyy", mBirthday, Date) End PropertyPublic Sub speak() ' 這個方法在后面沒有使用,類中可以定義而已MsgBox "我姓名是" & name & "今年" & age & "歲了!" End Sub這樣就在VBA中定義了學生類,其中實現了類的部分封裝特性。對于需要封裝的屬性,需要對多個變量名進行區分,例如:name、mName和aName,這點在java或python比較方便。
屬性方法有三個:
Public Property Let|Set 方法名 ( 參數 as 類型) Public Property Get 方法名() As 返回類型'其中Let和Set選一個,依據是傳遞過來的參數類型是否是對象,如果是使用Set,否則,使用Let。然后,插入模塊(普通模塊)進行測試,由于以下示例中使用了字典對象,想要使用該工具需要通過菜單 工具–> 引用后才可用,可以參考 Excel VBA 中使用集合和字典對比總結 :
代碼區輸入如下代碼,進入調試模式,可以看到所有的學生對象已經進入字典中:
在VBA編程中,對于處理這種多條記錄的,我一般會在類模塊中先定義一個類,用以保存表中各條記錄的各個字段內容,類屬性名對應于excel表中的字段名,不想直接使用數組編程,而是利用數組獲取表中的記錄,然后把各條記錄的內容逐個賦值給基于定義類的創建的對象屬性中,然后把對象逐個保存到字典中后使用。這是因為直接采用數組,在編程中那個位置對應什么屬性不直觀,使用對象的屬性來記錄數據直觀,利于理解和今后的維護。例如在編程中看到 student.name 與 arrStudent(i,2) 兩種不同的表示形式,喜歡哪一個?!
三、類詳細定義
前面我們已經看到了類的一些基本定義,下面詳細了解一下類定義中的細節。下面把大部分屬性設置成私有的,需要通過屬性方法賦值和獲取,實現類的封裝特性。
在我們插入類模塊時,VBA默認繼承了2個事件:初始化事件(Class_Initialize),銷毀事件(Class_Terminate)。我們可以從代碼區上側看到:
Class_Initialize初始化事件(構造方法),顧名思義,是在類實例化的時候自動觸發,就在執行set objStudent = New Student這條語句的時候觸發。類的初始化事件是我們初始化類屬性的最佳場合,例如可以在該方法內,設置一些默認值。而銷毀事件(Class_Terminate)用于在銷毀對象時,把一些資源予以清理。但是這種事件不能傳遞參數,即不能在實例化時傳遞參數。
下面給出比較詳細的定義:
'Student 類代碼 Option Explicit '變量要求顯示聲明才能使用 ' 類模塊代碼區 Public id As Integer ' 唯一代表學生的學號,因為姓名可能會重名, 聲明 id 為整數 Private mName As String ' 姓名,聲明name 為字符串,public代表公共的,表示創建的對象這個屬性可以直接訪問和修改 Private mBirthday As Date ' 出生日期,聲明為日期, m表示me,由于沒有this.birthday這種形式,只能在變量名上做文章 Private mGender As enuGender ' 性別,設置為枚舉類型Public Enum enuGender ' 對性別進行枚舉類型(Enumeration)聲明Female = 0 ' 女Male = 1 ' 男 End EnumPrivate Sub Class_Initialize() ' 構造方法,就是在實例化時該方法會被觸發Debug.Print "對象創建初始化!" ' 這種特殊的方法是事件,不能傳遞參數mGender = enuGender.Male ' 可以設置默認值,例如性別默認為男 End SubPrivate Sub Class_Terminate() ' 析構時間,對象被銷毀時,該方法會被觸發Debug.Print "對象被銷毀了!" End SubPublic Property Let name(aName As String) ' 姓名name屬性的setter方法,可以控制賦值過程If Len(aName) <= 4 ThenmName = aNameElseErr.Raise 5555, "Student Class module", "姓名不能超過4個字符"End If End PropertyPublic Property Get name() As String ' 姓名屬性的getter方法,name = mName End PropertyPublic Property Let birthday(aBirthday As Date) ' aBirthday中第一字符a表示為參數argumentIf aBirthday < DateAdd("yyyy", -130, Date) Or aBirthday >= Date ThenErr.Raise 5555, "Student Class module", "年齡范圍不合法" ' 如果不符合要求,拋出一個自定義異常ElsemBirthday = aBirthdayEnd If End PropertyPublic Property Get birthday() As Date ' 生日屬性的getter方法birthday = mBirthday End PropertyPublic Property Get age() As Integer ' 生日屬性的getter方法(這里沒有使用set ,如果要賦值的是對象的,把Get換成Set)age = DateDiff("yyyy", mBirthday, Date) End PropertyPublic Property Get gender() As enuGendergender = mGender End PropertyPublic Property Let gender(aGender As enuGender)Static blnFlag As BooleanIf blnFlag = False ThenblnFlag = TruemGender = aGenderElseErr.Raise 5555, "Student Class module", "性別一旦設定,就無法修改"End If End PropertyPublic Sub speak() ' 類中定義普通方法MsgBox "我姓名是 " & mName & " 今年 " & age & " 歲了!" End Sub關于封裝
以上Student類中除了id屬性外,其他屬性都予以封裝。性別屬性使用了枚舉類型,便于編程時可以自動提示,更加方便和專業。同時,為了防止對性別任意修改,只允許賦值一次,這里使用了 static 修飾符,能夠確保在excel未關閉的前提下,不能進行第二次修改。姓名屬性要求輸入的字符個數必須大于4個,否則,拋出一個自定義異常。出生日期屬性要求輸入的日期必須符合一定的要求,使產生的年齡在1~130之間。正是有了屬性方法,才能實現對賦值的檢查,以及實現輸入出生日期,就可以得到年齡屬性值。對象編程中的一個思想是,對象本身的屬性或方法,由對象自身來完成,例如年齡的計算在類中完成。
用于封裝的屬性方法是比較特殊的:按理說,既然是方法,那么在使用時應該使用方法調用,例如:
objStudent.speak ' vba中普通方法的調用可以不加小括號,后面加空格后直接提供參數(如果有參數時)而屬性方法的使用就不一樣,就好像不是方法,而是屬性,例如:
objStudent.gender = male關于多態
前面提到了字典對象的使用,在聲明時可以使用以下方式,先把變量dic定義為對象的老祖宗 object 類型,再使用CreateObject方式創建字典對象,然后把字典的引用賦值給變量,在這個過程中,其實是使用了對象的多態特性。object類型的變量被賦值了具體的對象的引用。
dim dic as object ' 聲明dic為object類型 set dic = CreateObject("scripting.dictionary") ' 創建一個字典對象,并賦值給 變量 dic前面使用了這樣的聲明:
Dim arrStudent() As Variant ' 聲明一個數組這里的 Variant 是一種數據類型,是未明確聲明為某一其他類型(例如,integer、string等)的所有變量的數據類型。Variant 是一個特殊數據類型,它包含除固定長度 String 數據以外的任何類型的數據(包含現在支持用戶定義的類型)。Variant 還可以包含特殊值 Empty、Error、Nothing 和 Null。 可以使用 VarType 函數或 TypeName 函數來確定如何對待 Variant 中的數據。這種方法的使用是否有點類似于多態,不過VBA不使用這種說法。
關于異常
大家應該遇到過,例如出現 2 / 0 ,就會拋出一個除數是0的異常。自定義異常非常簡單,具體提示為:
Err.Raise Number:=vbObjectError + 513, Source:="Student Class module", Description:="年齡范圍不合法"解釋:
測試:
' 普通模塊 Public Sub testStu()Dim objStudent As New Student ' 聲明一個學生類對象' Dim objStudent As Student ' 使用這樣聲明可以 ' Set objStudent = New Student ' 實例化對象objStudent.birthday = #1/1/1966# ' 給對象的出生日期屬性賦值objStudent.id = 1001 ' 給對象的學號賦值objStudent.name = "john" ' 給對象的姓名賦值objStudent.speak ' 調用對象的普通方法End Sub運行這段代碼,就會跳出一個:
在調試模式下,可以看到對象的屬性值:
關于自定義事件
前面示例了繼承自object類的兩個事件,下面演示如何進行自定義事件,從代碼上看是出乎意料的簡單。在VBE中插入一個類模塊,并更名為 TimerState。
在右側代碼區域輸入:
從語法上看,自定義事件的關鍵字是 Event,以上代碼定義了兩個事件,其基本語法:
[ Public ] Event procedurename [ (arglist) ]public 是可選的,procedurename 必需的,是事件的名稱(過程名),后面是參數列舉(如果有參數的話)。
然后,插入用戶窗體Form1,并在上面設置一個標簽Label1,兩個文本框,Text1和Text2,一個命令按鈕Cammand1,以上控件需要在屬性窗口中的(名稱)中設置成以上的名稱。
右鍵查看代碼,輸入以下代碼:
以上語句中(Private WithEvents mText As TimerState)就是進行變量類型聲明,這里使用了 WithEvents 關鍵字,其含義是同時帶上事件,如果沒有使用該關鍵字,TimerState類中的事件不能使用。
Private WithEvents mText As TimerState ' mText 聲明為 Timerstate 事件類型
如果在聲明時不帶關鍵字 WithEvents ,則沒有相應的事件。
以上代碼使用了關鍵字 DoEvents ,其含義是 交出執行控制權,以便操作系統能夠處理其他事件
DoEvents ' 交出執行控制權,以便操作系統能夠處理其他事件啟動程序后,出現以下界面:
四、結論:
VBA的類的定義和使用對于編程幫助較大,建議予以使用。只是各種語言的語法各異,這里也是給自己進行記錄,便于今后查詢使用。
總結
以上是生活随笔為你收集整理的VBA 类模块理解和使用总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: karaf测试小记
- 下一篇: Windows下目录下文件批量重命名