日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

VBA类之一(初识类)

發(fā)布時間:2023/12/10 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 VBA类之一(初识类) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第一章 開頭篇
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???——認識類



? ?? ? Visual Basic是基于對象的編程(注:本文所有的代碼和討論將都以VB為基礎模型,不過我會盡量使用一些大家在VBA中常見的例子來做說明的。),所以我們常見的一些東西其實都與類有關。不需驚訝,是的,類其實離我們很近,它們正和我們天天相處。可以夸張的說,如果離開了類,我們的VB(VBA)就無用武之地了。
? ?? ???我們還是先來看一條非常簡單的語句,讓我們親身體會一下類與我們的距離:

  • ?
  • '例一
  • Sub Hello()
  • ??Range("A1").Value = "Hello,world"
  • End Sub
  • 復制代碼


    ? ?? ???我們就暫且稱之為VBA的Hello world吧,看到這個簡單到不能再簡單的例子,或許你笑了,這個根本就和類沒有關系嘛。是的,我們在這里并沒有明顯的看到類,但是我們看到了Range("A1")這個對象,而在Range("A1")前面,還隱藏著一個對象:ActiveSheet,而在ActiveSheet前面又有一個ActiveWorkbook,而在ActiveWorkbook前面還有一個Application。是的,這些都是對象,我們依舊沒有看到類。先別急,我們再從后面往前面看一遍:Range("A1").Value="Hello,World"? ?Value是什么?Value是一個Range("A1")這個對象的一個屬性。那它是從哪來的呢?它是由Range類定義的,是的,我們發(fā)現了第一個出現在我們的面前的類?Range。你或許一下還接受不了,你剛剛在前不是還說Range("A1")是一個對象嗎?怎么現在又說Range又是一個類呢。呵呵,不著急,我們還是看看下面的這個例子,或許你就很快就會明白了。

  • ?
  • '例二
  • Sub Hello()
  • ??Dim Range1 As Range? ?? ?? ?? ? '引用一個Range類
  • ??Set Range1 = Range("A1")? ?? ? '將類實例化
  • ??Range1.Value = "Hello,world"? ?'這時,Range1又是一個對象了
  • ??Set Range1 = Nothing? ?? ?? ?? ?? ? '銷毀一個類的實例
  • End Sub
  • 復制代碼

    ? ?? ? 或許你現在已經發(fā)現了,我們引用了一個Range類,并將之實例化后修改了它的屬性。而在例一中,我們只不過是將這一切都以隱藏起來,直接對一個對象Range("A1")修改它的屬性,但Range("A1")這個對象正是引用了Ragne這個類,才具有了Range類的屬性"Value"。類本身并不直接為我們做什么,但是,它卻又一直默默的隱藏在幕后規(guī)化著我們的動作。是的,這就是類,它往往都是通過對象的方式展現在我們的面前,讓我們無時無刻與之交流,卻又常常在不經意間忽視了它的存在。現在再回到例一去看,就會很容易的發(fā)現,ActiveSheet實際引用了Worksheet類、ActiveWorkbook引用了Workbook類,而Application則引用了和它同名的Application類(這也正是我們會經常被混淆一個概念,一個對象可以和被它所引用的類同名),原來我們在短短一個賦值的語句中,已經在與這么多的類打交道。
    ? ?? ???類就像是我們呼吸的空氣,我們一直深在其中,卻又常常忘卻了它的存在。那我們又要怎樣來區(qū)分類和對象呢?其實它們經常成對的出現在我們面前,只是一個看得見摸得著的,一個卻深藏不露。我們可以這樣去理解類與對象:類是一個概念或是一種定義,每個類擁有其自己的特征和行為方式,而對象就是某個類的實例。就像"人","人"是一個定義,你一看到"人"就會想到它是直立行走、有頭、五官……等各種特征,但當"人"被具體到你、我、或是某一個人的時候,它就是一個對象了。
    ? ?? ???既然類與我們和程序息息相關,那么我們更應該去好好的了解它了。讓我們準備好行囊,到神秘的類的領地去好好瀏覽一番吧。


    第二篇 走近類
    ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???——類的基本寫法及注意項



    ? ?? ???如果說上一篇讓我們看到類隱藏在神秘面紗后的面孔,那么現在我們將要做的就是真正的看清楚它。不過,在此之前我還是要讓大家再次熟悉一下面的幾個關鍵詞:

    Public? ?? ?? ?? ?? ? 外部,可供類或是模塊自身外調用;
    Private? ?? ?? ?? ?? ?內部,只能由類或是模塊內部直接調用;
    Friend? ?? ?? ?? ?? ? 友元,只能由工程內調用(即:在當前工程內部調用時相當于Public,而對外部工程相當于Private。)。
    SubFunction??過程(不返回值)和函數(返回值),在類中我們可將其視為類的方法;
    Property Get? ?? ?返回屬性的值;
    Property Let? ?? ?設置屬性的值;
    Property Set? ?? ?設置對象屬性的值(即:該屬性含有對象引用)。
    ? ?? ???[注:Property往往是成雙出現在類中的某一個屬性上,甚至可能三者同時出現,當一個類中某個屬性只有Property Get時代表這一屬性為只讀屬性,比如在VB工程中Form類的hWnd屬性。]
    Event? ?? ?? ???定義用戶自定義的事件
    ? ?? ???[注:Event可以像聲明過程的參數一樣來聲明事件的參數,但有以下不同:事件不能有帶命名參數、Optional?參數、或者?ParamArray參數。事件沒有返回值。];
    RaiseEvent? ?? ?引發(fā)在一個類、窗體、或者文檔中的模塊級中聲明的一個事件。
    Implements? ?? ?指定要在包含該語句的類模塊中實現的接口或類。


    ? ?? ? 當我們熟悉上面的幾個關鍵詞后,再來分析類,就會發(fā)現類其實也挺簡單。我們將從一個簡單的類開始來正式的學習類的定義和使用方法。不過在此之前,我建議大家先去讀讀qee用兄寫的一個帖子:VBA類:隱者的秘密qee用兄在那個帖子中圖文并茂,已經將類清晰的描繪出來,并親自帶領我們寫了一個類。在此,讓我們向qee用兄道聲辛苦,并送上萬分感激。好了,現在,我們來寫一個非常簡單的類,用它來實現兩個數的四則運算。

  • ?
  • '類的名稱定義為:四則運算
  • Option Explicit
  • ?
  • Public Event OnError(ByVal Number As Long, ByVal Description As String, ByVal Source As String)
  • ?
  • Private lNum1 As Double, lNum2 As Double
  • ?
  • Public Property Let Number1(ByVal Number As Double)
  • ??lNum1 = Number
  • End Property
  • ?
  • Public Property Get Number1() As Double
  • ??Number1 = lNum1
  • End Property
  • ?
  • Public Property Let Number2(ByVal Number As Double)
  • ??lNum2 = Number
  • End Property
  • ?
  • Public Property Get Number2() As Double
  • ??Number2 = lNum2
  • End Property
  • ?
  • Public Property Get 和() As Double
  • ?
  • ??On Error GoTo Error01
  • ??
  • ??和 = lNum1 + lNum2
  • ??Exit Property
  • Error01:
  • ??RaiseEvent OnError(Err.Number, Err.Description, Err.Source)
  • ??Err.Clear
  • ??和 = 0
  • End Property
  • ?
  • Public Property Get 差() As Double
  • ?
  • ??On Error GoTo Error02
  • ??
  • ??差 = lNum1 - lNum2
  • ??Exit Property
  • Error02:
  • ??RaiseEvent OnError(Err.Number, Err.Description, Err.Source)
  • ??Err.Clear
  • ??差 = 0
  • End Property
  • ?
  • Public Property Get 積() As Double
  • ?
  • ??On Error GoTo Error03
  • ??
  • ??積 = lNum1 * lNum2
  • ??Exit Property
  • Error03:
  • ??RaiseEvent OnError(Err.Number, Err.Description, Err.Source)
  • ??Err.Clear
  • ??積 = 0
  • End Property
  • ?
  • Public Property Get 商() As Double
  • ?
  • ??On Error GoTo Error04
  • ??
  • ??商 = lNum1 / lNum2
  • ??Exit Property
  • Error04:
  • ??RaiseEvent OnError(Err.Number, Err.Description, Err.Source)
  • ??Err.Clear
  • ??商 = 0
  • End Property
  • ?
  • Public Sub ClearNumber()
  • ??lNum1 = 0: lNum2 = 0
  • End Sub
  • 復制代碼

    ? ?? ? 然后,將下面的代碼寫到ThisWorkbook模塊中:

  • ?
  • Option Explicit
  • ?
  • Private WithEvents Class1 As 四則運算
  • ?
  • Sub TestClass()
  • ??If Class1 Is Nothing Then Set Class1 = New 四則運算
  • ??Class1.ClearNumber
  • ??Class1.Number1 = 9
  • '??Class1.Number2 = 8
  • ??Debug.Print Class1.和
  • ??Debug.Print Class1.差
  • ??Debug.Print Class1.積
  • ??Debug.Print Class1.商
  • End Sub
  • ?
  • Private Sub Class1_OnError(ByVal Number As Long, ByVal Description As String, ByVal Source As String)
  • ??MsgBox "類中發(fā)生錯誤,錯誤代碼:" & Number & " 錯誤提示:" & Description & " 錯誤源:" & Source
  • End Sub
  • 復制代碼

    ? ?? ? 這是一個非常簡單的類的定義和應用(當然,實際編程時很少有人會為了四則運算而專門寫個類。),雖然它很簡單,不過它卻幾乎包含了類所有常見的特征:屬性、方法、事件。運行TestClass后我們可以看到立即窗口中的輸出,并接收到一個MsgBox的窗體,它提示了我們在類運行過程中產生了一個除數為零的錯誤,而這正是類中的OnError事件激發(fā)的。
    ? ?? ? 雖然這個類本身沒有什么實際的應用的意義,但是我們卻可以用它來做一個模版,我們只要參照這個類,就可以很快的寫出自己想要的類。
    ? ?? ? 你現在是不是已經有了自己馬上要編寫一個類的沖動呢?那么我們開始準備動手吧,不過在你動手之前,我這里還要給你幾點關于類的建議(這些都是筆者自身積累的經驗或是前輩們的忠告。):

    ? ?? ? 一、在正式編寫一個類的前期,最好盡可能規(guī)定好所有要用到的接口與方法。一旦一個類被正式發(fā)行后(封裝成DLL并被其它的工程引用),后期維護時盡量不要再去修改已有的接口函數及其參數,除非你打算將所有的工程全部重寫;
    ? ?? ? 二、為你類中的屬性、方法、事件取個有意義的名字(特別是聲明為Public方式的),如果允許的話,最好使用“工具”->“過程屬性”為其添加一些描敘,說明它的意義或是調用方式,這樣可以方便查看其屬性并讓客戶(甚至是自己)能很快明白這個函數的用途;
    ? ?? ? 三、只暴露必須的接口供外部調用,不要將一些可能僅在類內部使用屬性和方法暴露給類外部;
    ? ?? ? 四、不到萬不得已,不要在類中定義Public方式的變量;
    ? ?? ? 五、重新編譯新版本的DLL時,最好是按“版本相同版本兼容版本不兼容”這個次序來選擇編譯后DLL版本兼容性,即:能用“版本相同”方式就不要用“版本兼容”,更不要用“版本不兼容”,因為“版本不兼容”方式就意味著你的舊工程在其重新編譯前將無法使用這個DLL。一旦準備使用無兼容性版本,那么就要考慮如何減少部件的用戶以后可能遇到的麻煩。如果以后的版本還要做一些可能破壞兼容性的修改,那么最好把這些修改集中在一次進行。計劃對不兼容進行的修改時,把工程作為一個新的任務來對待,應該投入盡可能多的精力進行計劃,就象在創(chuàng)建新的部件。創(chuàng)建不兼容版本,這要求三個步驟:改變工程名,改變文件名以及通過選定不兼容進行編譯(注:這三個步驟最好是一定要做到,否則將會讓我們在后面安裝引用此類時會發(fā)生各種問題而導致我們無法使用類);
    ? ?? ? 六、可能的話,不要忘了在類內部添加錯誤處理過程,盡管這些錯誤處理過程可能會損耗我們的運行時間,但它們能讓我們的類更健壯;
    ? ?? ? 七、類中不能定義Public方式的結構(Type),如果必需要這么做的話,可以使用一個類來代替Type
    ? ?? ? 八、在類的Class_Terminate()事件中釋放被當前類所引用的類或是數據。當我們某個類的實例已經不再需要時,請將其使用?Set[I]Object[/I] =?Nothing?的方式來銷毀它;
    ? ?? ? 九、……(留給大家填吧)……








    ?


    第三篇 為什么要使用類
    ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???——類的作用及其重要性



    ? ?? ? 現在,我們已經初步的認識了類,甚至我們已經可以自己開始寫自己的類了。但是我們卻還不是很清楚,類給我們帶來了什么?我們?yōu)槭裁匆妙?#xff1f;我們就先來看看類的好處吧。
    ? ?? ? 1、代碼重用;
    ? ?? ? 2、降低程序的藕合度;?
    ? ?? ? 3、增強程序的可拓展性;
    ? ?? ? 4、易修改性;
    ? ?? ? 5、……

    ? ?? ? “藕合度”指的是程序模塊間存在聯系的緊密程度。如果一個程序各模塊存在的聯系太緊密時,就意味后期的修改將會非常復雜,甚至于有時為了修改某一個代碼功能時可能要全部工程文件都修改一次,并且由于各模塊間的函數與變量相互相交調用,嚴重的影響了程序的可讀性。自然,我們不希望發(fā)生這種情況。而類正是我們規(guī)避這個情況發(fā)生的首選工具。因為類對于外部而言,我們所需要的僅僅是調用類的接口函數,而類內部的結構與運行我們并不需要去關心它。就像一個用來處理數據的類,我們只要約定好類的接口,就可以在外部直接獲取或是修改我們想要的數據。但是數據存在哪,用什么方式或是結構組成我們根本就不需要了解,我們所要做的只是在外部直接調用就好了。這就意味著我們一旦約定好了接口,就可以同時開始開發(fā)前臺與后臺的數據處理塊。而且,當我們需要修改數據的存儲方式時,也會變得更加簡單。比如:我們現有一個處理數據的類,它目前是使用文件來存儲數據,現在我們需要把數據存儲改為SQL數據庫,這時我們要做的僅僅是在類內部修改要數據的處理過程,而對于整個工程而言,我們甚至不用做任何的修改就可以直接用了。這樣對于大型工程而言,猶為重要,因為我們常常把類封裝成DLL,這就意味我們僅僅只需要重新編譯這個DLL,然后發(fā)布給用戶升級就完成了這次修改,否則的話我們只能將整個工程全部修改后重新發(fā)布給用戶才能使用。如果你經常使用VBA的話就一定會常常遇見Range這個類,Office每一次大版本升級,其內部的文件結構都會有變動,但是我們在VBA中對Range的操作卻無需更改就可以直接使用了,這個正是因為Range類雖然內部改動了,但是它的接口還是一樣,所以我們原有的VBA工程并不需要改動就可以直接使用了。
    現在我們再來通過一個選中區(qū)域高亮顯示的例子來看看類的其它幾個特性吧:

  • ?
  • Option Explicit
  • ?
  • Private Type TypeRageLast
  • ??Range? ?? ? As Range
  • ??ColorIndex??As Long
  • End Type
  • ?
  • Private Const ColorIndex = 5&
  • Private RangeLast() As TypeRageLast
  • ?
  • Private Sub Worksheet_SelectionChange(ByVal Target As Range)
  • ??Dim I As Long
  • ??Dim Range1 As Range
  • ??
  • ??On Error Resume Next
  • ??If UBound(RangeLast) = -1 Then
  • ? ? ReDim RangeLast(0)
  • ??ElseIf UBound(RangeLast) Then
  • ? ? For I = 1 To UBound(RangeLast)
  • ? ?? ?If RangeLast(I).Range.Interior.ColorIndex = ColorIndex Then
  • ? ?? ???RangeLast(I).Range.Interior.ColorIndex = RangeLast(I).ColorIndex
  • ? ?? ?End If
  • ? ? Next I
  • ??End If
  • ??
  • ??I = 0
  • ??ReDim RangeLast(0)
  • ??
  • ??For Each Range1 In Target
  • ? ? I = I + 1: ReDim Preserve RangeLast(I)
  • ? ? RangeLast(I).ColorIndex = Range1.Interior.ColorIndex
  • ? ? Set RangeLast(I).Range = Range1? ??
  • ??Next Range1
  • ??Target.Interior.ColorIndex = ColorIndex
  • End Sub
  • 復制代碼

    這段代碼的功能很簡單(我們在這里并不去討論它的功能),只是在工作表中,選中某個區(qū)域后就把該區(qū)域的背景色設置為并且恢復上次被選中區(qū)域的原有背景色。我們只要把上面的代碼拷貝到我們需要的工作表的代碼中就可以實現這個功能了。但是,當我在實際使用過程中就會發(fā)現一些新的問題:
    ? ?? ? 1、如果我們的工作簿中有200個工作表需要這個功能,我們就要復制200次——暈倒……
    ? ?? ? 2、復制完200次后,我們再在各個工作表中實際測試一下。就會發(fā)現新的問題:當我們在各個工作表之間切換焦點時,原工作表中最后一次被高亮的區(qū)域沒有被還原!現在我們需要修改代碼——在每一個工作表中Deactivate事件中加上相應處理代碼。天啊,又得200次……又要暈了吧?
    ? ?? ? 3、文檔開始正式使用了,但是當我們新增一個工作表后發(fā)現:我們又得重新切換到VBA中再復制一次代碼!如果這個文檔是給客戶使用的,難道我們還要要求客戶每次增加工作表時自己去復制VBA代碼?……這次徹底暈了吧?^_^
    ? ?? ? 難道就沒有更方便的方法嗎?

    ? ?? ? 當然有了。我們就用類來試試吧,我們先建一個clsSelHeightColor類:

  • ?
  • ‘類名:clsSelHeightColor
  • Option Explicit
  • ?
  • Private Type TypeRageLast
  • ??Range? ?? ? As Range
  • ??ColorIndex??As Long
  • End Type
  • ?
  • Private Const ColorIndex = 5&
  • Private WithEvents mSheet As Worksheet
  • Private WithEvents Workbook??As Workbook
  • Private RangeLast() As TypeRageLast
  • ?
  • Private Sub mSheet_Activate()
  • ??Dim I As Long
  • ??Dim Range1 As Range
  • ??
  • ??On Error Resume Next
  • ??I = 0
  • ??ReDim RangeLast(0)
  • ??
  • ??For Each Range1 In Selection
  • ? ? I = I + 1: ReDim Preserve RangeLast(I)
  • ? ? RangeLast(I).ColorIndex = Range1.Interior.ColorIndex
  • ? ? Set RangeLast(I).Range = Range1
  • ? ? Range1.Interior.ColorIndex = ColorIndex
  • ??Next Range1
  • End Sub
  • ?
  • Private Sub mSheet_Deactivate()
  • ??mSheet_SelectionChange Nothing
  • End Sub
  • ?
  • Private Sub mSheet_SelectionChange(ByVal Target As Range)
  • ??Dim I As Long
  • ??Dim Range1 As Range
  • ??
  • ??On Error Resume Next
  • ??
  • ??If UBound(RangeLast) Then
  • ? ? For I = 1 To UBound(RangeLast)
  • ? ?? ?If RangeLast(I).Range.Interior.ColorIndex = ColorIndex Then
  • ? ?? ???RangeLast(I).Range.Interior.ColorIndex = RangeLast(I).ColorIndex
  • ? ?? ?End If
  • ? ? Next I
  • ??End If
  • ??
  • ??I = 0
  • ??ReDim RangeLast(0)
  • ??
  • ??For Each Range1 In Target
  • ? ? I = I + 1: ReDim Preserve RangeLast(I)
  • ? ? RangeLast(I).ColorIndex = Range1.Interior.ColorIndex
  • ? ? Set RangeLast(I).Range = Range1
  • ??Next Range1
  • ??Target.Interior.ColorIndex = ColorIndex
  • End Sub
  • ?
  • Public Property Set Sheet(ByVal mSheet1 As Worksheet)
  • ??If Not (mSheet Is Nothing) Then mSheet_SelectionChange Nothing
  • ??Set mSheet = mSheet1
  • ??Call mSheet_Activate
  • End Property
  • ?
  • Public Property Get Sheet() As Worksheet
  • ??Set Sheet = mSheet
  • End Property
  • ?
  • Private Sub Class_Initialize()
  • ??ReDim RangeLast(0)
  • End Sub
  • ?
  • Private Sub Class_Terminate()
  • ??mSheet_SelectionChange Nothing
  • ??Erase RangeLast
  • End Sub
  • 復制代碼

    然后再把下面的代碼復制到ThisWorkbook中的代碼框中:

  • ?
  • Option Explicit
  • ?
  • Dim mSheets As Collection
  • ?
  • Private Sub Workbook_BeforeClose(Cancel As Boolean)
  • ??Set mSheets = Nothing
  • End Sub
  • ?
  • Private Sub Workbook_NewSheet(ByVal Sh As Object)
  • ??Dim clsSheet As New clsSelHeightColor
  • ??
  • ??Set clsSheet.Sheet = Sh
  • ??mSheets.Add clsSheet
  • ??Set clsSheet = Nothing
  • End Sub
  • ?
  • Private Sub WorkBook_Open()
  • ??Dim clsSheet As clsSelHeightColor
  • ??Dim Sheet As Worksheet
  • ??
  • ??On Error Resume Next
  • ??
  • ??Set mSheets = New Collection
  • ??For Each Sheet In Sheets
  • ? ?? ?Set clsSheet = New clsSelHeightColor
  • ? ?? ?Set clsSheet.Sheet = Sheet
  • ? ?? ?mSheets.Add clsSheet
  • ? ?? ?Set clsSheet = Nothing
  • ??Next
  • End Sub
  • 復制代碼

    保存后重開工作簿測試……嘿嘿,全部搞定!而且當我們要增加的Deactivate事件中的代碼僅僅只需類中添加一次,只有一行代碼(上面的類中已經添加好了),方便吧?當我們新增工作表時,也不再需要去復制一次代碼了,一行代碼都不用寫了就已經實現了這個功能。當其它工作簿也需要這個代碼時,我們只要在ThisWorkbook代碼中增加相應的代碼,并導入這個類就行了,比一個一個表去復制代碼方便多——一想到相同的代碼要在每個工作表中復制一次就頭暈了。
    ? ?? ? 在此,我們對類的功能和特性有了一定的了解,不過,還有更多的東西在等待我們去發(fā)現。當下面向對象編程如此風行,類在其中的功勞是無與倫比的。
    ? ?? ? 現在,還猶豫什么?快去和類做一次“0距離接觸”吧……

    ? ?? ? 在此,此文也終于要作一個小小的終結了。大家也跟隨我初步對類作了點了解,也算是對類有了個第一印象吧。當然,類的高級應用和復雜型的類我們并沒有去了解,或許哪天你會引領我去瀏覽一番?
    ? ?? ? 我也謝謝大家浪費不少時間來看完這個帖子,如果對我有意見或是覺得我在胡說八道要拍我磚頭的,請下手留點情——我很怕疼,有意見扔扔雞蛋或是RMB就好了。如果覺得看完對你有所幫助的話就幫頂下吧,或是你看完有什么問題的話也可以直接問,只要我會的我一定回(太難的問題還請恕我無能為力)。

    ?

    ?

    總結

    以上是生活随笔為你收集整理的VBA类之一(初识类)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。