| 導購 | 订阅 | 在线投稿
分享
 
 
 

集合類:VBA集合對象的安全包裝

來源:互聯網  2008-06-01 01:15:25  評論

你遲早都要管理一組由VBA類模塊創建的自定義對象,VBACollection對象是實現這一目的的理想工具。我們在該雜志的PRemiere期刊中曾經介紹過Collections,並且闡述了如何使用Collection對象來存儲其他對象。雖然該項技術十分有用,但它確實還存在一些局限。在本文中,我們討論了這些局限,並向你展示如何通過使用(你猜是什麽)類模塊來克服這些局限。

Collections中遇到的麻煩

Collections對象中到底有什麽問題呢?它似乎存儲對象存儲得非常好。事實上,它工作起來的確是太好了。Collections對象得最大問題是它可以存儲到任何數據類型得引用,例如Integer,String,Long,Variant,等(見圖1)。

當然,如果你能保證每個對象都具有相同得類型,並且具有相同得屬性何方法的話,那麽在Collection中處理對象就變得異常簡單。例如,作爲本月範例ExcelVBA項目的組成部分,我們創建一個自定義的File類來代表一個磁盤文件。這個File類有幾個屬性,其中包括Path,Size,和ShortName。如果你創建了一個File對象的Collection,那麽你將希望這個Collection中的每個對象都具有這些屬性。你可以毫無異議地使用與下面類似的代碼:

'PrintthesizeofthefirstfileintheCollection.

Debug.PrintcolFiles.Item(1).Size

但是,如果colFilesCollection中的第一個對象不是File,將會出現什麽情況呢?如果它是一個Form或Control對象,又會怎樣呢?如果它根本就不是一個對象呢?當然,當它試圖執行該代碼的時候,VBA將生成運行時間錯誤。

集合類:VBA集合對象的安全包裝

圖1(左)VBACollection對象可以容納任何類型的對象

集合類:VBA集合對象的安全包裝

圖2(右)創建一個Collection類來防止不想要的對象進入集合

輸入Collection類

Collection類可以充當Collection對象的過濾器,限制你可以在裏面存儲的對象類型(如圖2所示)。通過與你的應用程序中的Collection類(二不是Collection對象本身)的互相作用,你可以防止不想要的對象的進入。創建和使用Collection類允許你擴展Collection對象的屬性和方法(後面我們將舉例說明)。

爲了說明Collection類以及如何創建一個Collection類,我們將使用帶有通過掃描磁盤目錄創建的文件集合的Excel97項目。圖3顯示了一個表單,它用你選擇了路徑之後存儲在自定義Collection類中的文件信息使得列表框通俗化。

這個表單使用一個叫做Files的Collection類來通俗化列表框。在創建Collection的時候,以向你的VBA項目添加一個新的類模塊作爲開始,然後在這個新模塊的聲明段聲明一個PrivateCollection對象。下面是來自Files類模塊的聲明:

'Collectionoffiles.

PrivatepcolFilesAsNewCollection

爲了將Collection對象與外界"隔絕",並阻止程序的其他部分用無用的東西填充它,必須將Collection對象聲明爲Private。

複制所需要的方法

當然,一旦你已經將Collection對象聲明爲Private,那麽對于任何過程都沒有辦法向其中添加項目或從中刪除項目了。因而,下一步就是複制Collection對象的標准方法了。盡管聽起來象是做了大量額外的工作,實際上仍然在做我們前面所提及的"過濾"工作。

請記住,內建的Collection對象具有Add方法,該方法接受對象引用和獨特的、包括文字與數字的標識符。如果你的應用程序正在直接使用Collection對象,那麽它將極有可能創建對象的一個新的實例,並將其添加到Collection本身。

'Createanewinstanceofanobject.

DimobjFileAsNewFile

objFile.Path="C:AUTOEXEC.BAT"

'AddtoaCollectionobject.

colFiles.AddobjFile,objFile.ShortName

應用Collection類,應用程序調用該類的Add方法,傳遞任何必需的信息。請將先前的代碼與Files類的Add方法做一比較:

WithaCollectionclass,theapplicationcallstheAddmethodoftheclass,passinganyrequiredinformation.ContrastthepreviouscodewiththeAddmethodoftheFilesclass:

PublicFunctionAdd(PathAsString)AsFile

DimobjFileAsFile

'CreatethenewFileobject.

SetobjFile=NewFile

objFile.Path=Path

'AddittothePrivatecollection.

pcolFiles.AddobjFile,objFile.ShortName

'Returnapointertothenewobject.

SetAdd=objFile

EndFunction

在本例中,到Collection的對象創建和添加發生在Add方法內部;而類則保留了完整的控制。任何必需的信息(例如文件的路徑)是作爲參數向方法提供的。由應用程序調用將文件添加到Collection的代碼然後可以簡化爲:

'Addafiletothecollection.

colFiles.Add"C:AUTOEXEC.BAT"

InadditiontotheAddmethod,theCollectionclassshouldalsoimplementtheItemandRemovemethods,aswellasaCountproperty:

PublicFunctionItem(KeyAsVariant)AsFile

'Returnaniteminthecollection.

SetItem=pcolFiles.Item(Key)

EndFunction

PublicSubRemove(KeyAsVariant)

'Removeanitemfromthecollection.

pcolFiles.RemoveKey

EndSub

PropertyGetCount()AsLong

'Returnthenumberofitems.

Count=pcolFiles.Count

EndProperty

請注意,在這三種方法中,我們省略了錯誤處理--有些事情你是從來都不應該做的!至少應該包括一個錯誤處理器,通過使用Err對象的Raise方法來將錯誤傳遞、給調用過程。

圖3這個表單通過顯示文件信息來說明Collection類

'Privatevariabletostorepath.

PrivatepstrPathAsString

PropertyGetPath()AsString

'Returnstoredpathvalue.

Path=pstrPath

EndProperty

PropertyLetPath(strPathAsString)

DimstrFileAsString

'Clearthecollection.

SetpcolFiles=NewCollection

'Makesurethere'sabackslash.

IfRight(strPath,1)<>""Then

strPath=strPath&""

EndIf

'Getthefirstfile.

strFile=Dir(strPath&"*.*",_

vbReadOnlyOrvbHiddenOrvbArchiveOrvbSystem)

DoUntilLen(strFile)=0

'Addittothecollection.

CallAdd(strPath&strFile)

'Getthenextfile.

strFile=Dir()

Loop

'Savethepath.

pstrPath=strPath

EndProperty

圖4向Collection類添加Path屬性。將該屬性和類設置爲掃描目錄並將所找到的每個文件添加到私有Collection對象。

擴展Collection類

現在,你可以通過聲明Files類的一個實例來使用它了,並爲曾經由Dir函數找到的每個文件反複調用該類的Add方法。但這不是我們的例子要完成的工作。爲什麽不是呢?使用Collection類的一個優點是你可以通過添加更多的屬性和方法來擴展它的功能;不僅僅限于Add,Remove,Item,和Count。

在我們的Files類的情形中,它難道不對在類本身,而不是使用該類的每個應用程序裏面,放置掃描目錄的代碼做更多的檢測嗎?這是面向對象設計的一個指導原則:將代碼放在最靠近需要它的地方。

爲了闡述這個概念,我們向Collection類添加了一個Path屬性。當你設置這個屬性的時候,這個類將對目錄進行掃描,並向Collection對象添加它所找到的每個文件(見圖4)。

當一個過程改變該類的Path屬性的時候,將觸發PropertyLet過程。在我們的例子中,它發生在你從浏覽器對話框選擇了一個路徑之後。這裏是完成用文件列表填充Collection任務的簡單代碼。

'Reinitializethecollection.

SetmobjFiles=NewFiles

'Setthepathproperty.

mobjFiles.Path=strPath

一旦設置了Path屬性,Files類就將通俗化它自己的Collection,並且使它可以提供給應用程序。這已經是另一個例子了,在這個例子裏面,過程的"guts"存在于類自己內部(你可以證明類已不再需要它的Add和Remove方法了。在有些應用程序中,可能確實是這樣,但爲了達到說明的目的,我們已經選擇了留下它們作爲該類的一部分)。

一些不利之處

使用VBACollection類的生活並不總是美酒和玫瑰。當你使用Collection類代替Collection對象時,必須放棄兩樣東西。第一樣是Collection對象的默認方法,Item。默認的方法允許你從你的代碼中省略單詞"Item"。例如,如下兩條語句是一樣的,都是假定colFiles引用一個Collection對象。

Debug.PrintcolFiles.Item(1).Size

Debug.PrintcolFiles(1).Size

除非你正在使用的是VisualBasic5.0,否則沒有辦法爲一個類指定默認的方法。因而,你必須總是顯式調用Item方法。

Collection類的第二個主要的不足之處是不能創建列舉函數。列舉類就是可以使ForEach循環工作的類。如果你想重複Collection中的每一個項目,那麽就必須用老式的方法來完成,也就是使用Count屬性和ForNext循環。例如,下面的代碼可以通俗化列表框:

'Fillthelistboxwithinfo.

lstFiles.Clear

ForlngCount=1TomobjFiles.Count

WithmobjFiles.Item(lngCount)

lstFiles.AddItem.ShortName&_

Space(12-Len(.ShortName))&_

vbTab&.AttributeString&_

vbTab&.Size

EndWith

Next

請注意該過程是如何使用計數器變量lngCount來完成從1到Collection中項目數的循環的。With語句使用了Item方法來引用Collection中的每個對象。

請注意,VisualBasic5.0用戶可以通過創建列舉函數來克服這一局限。在VisualBasicBookOnline中搜索關鍵字"enumeration"可以得到更爲詳細的信息。-

你遲早都要管理一組由VBA類模塊創建的自定義對象,VBACollection對象是實現這一目的的理想工具。我們在該雜志的PRemiere期刊中曾經介紹過Collections,並且闡述了如何使用Collection對象來存儲其他對象。雖然該項技術十分有用,但它確實還存在一些局限。在本文中,我們討論了這些局限,並向你展示如何通過使用(你猜是什麽)類模塊來克服這些局限。 Collections中遇到的麻煩 Collections對象中到底有什麽問題呢?它似乎存儲對象存儲得非常好。事實上,它工作起來的確是太好了。Collections對象得最大問題是它可以存儲到任何數據類型得引用,例如Integer,String,Long,Variant,等(見圖1)。 當然,如果你能保證每個對象都具有相同得類型,並且具有相同得屬性何方法的話,那麽在Collection中處理對象就變得異常簡單。例如,作爲本月範例ExcelVBA項目的組成部分,我們創建一個自定義的File類來代表一個磁盤文件。這個File類有幾個屬性,其中包括Path,Size,和ShortName。如果你創建了一個File對象的Collection,那麽你將希望這個Collection中的每個對象都具有這些屬性。你可以毫無異議地使用與下面類似的代碼: 'PrintthesizeofthefirstfileintheCollection. Debug.PrintcolFiles.Item(1).Size 但是,如果colFilesCollection中的第一個對象不是File,將會出現什麽情況呢?如果它是一個Form或Control對象,又會怎樣呢?如果它根本就不是一個對象呢?當然,當它試圖執行該代碼的時候,VBA將生成運行時間錯誤。 [url=/bbs/detail_1783445.html][img]http://image.wangchao.net.cn/it/1323634884970.gif[/img][/url] 圖1(左)VBACollection對象可以容納任何類型的對象 [url=/bbs/detail_1783445.html][img]http://image.wangchao.net.cn/it/1323634884991.gif[/img][/url] 圖2(右)創建一個Collection類來防止不想要的對象進入集合 輸入Collection類 Collection類可以充當Collection對象的過濾器,限制你可以在裏面存儲的對象類型(如圖2所示)。通過與你的應用程序中的Collection類(二不是Collection對象本身)的互相作用,你可以防止不想要的對象的進入。創建和使用Collection類允許你擴展Collection對象的屬性和方法(後面我們將舉例說明)。 爲了說明Collection類以及如何創建一個Collection類,我們將使用帶有通過掃描磁盤目錄創建的文件集合的Excel97項目。圖3顯示了一個表單,它用你選擇了路徑之後存儲在自定義Collection類中的文件信息使得列表框通俗化。 這個表單使用一個叫做Files的Collection類來通俗化列表框。在創建Collection的時候,以向你的VBA項目添加一個新的類模塊作爲開始,然後在這個新模塊的聲明段聲明一個PrivateCollection對象。下面是來自Files類模塊的聲明: 'Collectionoffiles. PrivatepcolFilesAsNewCollection 爲了將Collection對象與外界"隔絕",並阻止程序的其他部分用無用的東西填充它,必須將Collection對象聲明爲Private。 複制所需要的方法 當然,一旦你已經將Collection對象聲明爲Private,那麽對于任何過程都沒有辦法向其中添加項目或從中刪除項目了。因而,下一步就是複制Collection對象的標准方法了。盡管聽起來象是做了大量額外的工作,實際上仍然在做我們前面所提及的"過濾"工作。 請記住,內建的Collection對象具有Add方法,該方法接受對象引用和獨特的、包括文字與數字的標識符。如果你的應用程序正在直接使用Collection對象,那麽它將極有可能創建對象的一個新的實例,並將其添加到Collection本身。 'Createanewinstanceofanobject. DimobjFileAsNewFile objFile.Path="C:AUTOEXEC.BAT" 'AddtoaCollectionobject. colFiles.AddobjFile,objFile.ShortName 應用Collection類,應用程序調用該類的Add方法,傳遞任何必需的信息。請將先前的代碼與Files類的Add方法做一比較: WithaCollectionclass,theapplicationcallstheAddmethodoftheclass,passinganyrequiredinformation.ContrastthepreviouscodewiththeAddmethodoftheFilesclass: PublicFunctionAdd(PathAsString)AsFile DimobjFileAsFile 'CreatethenewFileobject. SetobjFile=NewFile objFile.Path=Path 'AddittothePrivatecollection. pcolFiles.AddobjFile,objFile.ShortName 'Returnapointertothenewobject. SetAdd=objFile EndFunction 在本例中,到Collection的對象創建和添加發生在Add方法內部;而類則保留了完整的控制。任何必需的信息(例如文件的路徑)是作爲參數向方法提供的。由應用程序調用將文件添加到Collection的代碼然後可以簡化爲: 'Addafiletothecollection. colFiles.Add"C:AUTOEXEC.BAT" InadditiontotheAddmethod,theCollectionclassshouldalsoimplementtheItemandRemovemethods,aswellasaCountproperty: PublicFunctionItem(KeyAsVariant)AsFile 'Returnaniteminthecollection. SetItem=pcolFiles.Item(Key) EndFunction PublicSubRemove(KeyAsVariant) 'Removeanitemfromthecollection. pcolFiles.RemoveKey EndSub PropertyGetCount()AsLong 'Returnthenumberofitems. Count=pcolFiles.Count EndProperty 請注意,在這三種方法中,我們省略了錯誤處理--有些事情你是從來都不應該做的!至少應該包括一個錯誤處理器,通過使用Err對象的Raise方法來將錯誤傳遞、給調用過程。 圖3這個表單通過顯示文件信息來說明Collection類 'Privatevariabletostorepath. PrivatepstrPathAsString PropertyGetPath()AsString 'Returnstoredpathvalue. Path=pstrPath EndProperty PropertyLetPath(strPathAsString) DimstrFileAsString 'Clearthecollection. SetpcolFiles=NewCollection 'Makesurethere'sabackslash. IfRight(strPath,1)<>""Then strPath=strPath&"" EndIf 'Getthefirstfile. strFile=Dir(strPath&"*.*",_ vbReadOnlyOrvbHiddenOrvbArchiveOrvbSystem) DoUntilLen(strFile)=0 'Addittothecollection. CallAdd(strPath&strFile) 'Getthenextfile. strFile=Dir() Loop 'Savethepath. pstrPath=strPath EndProperty 圖4向Collection類添加Path屬性。將該屬性和類設置爲掃描目錄並將所找到的每個文件添加到私有Collection對象。 擴展Collection類 現在,你可以通過聲明Files類的一個實例來使用它了,並爲曾經由Dir函數找到的每個文件反複調用該類的Add方法。但這不是我們的例子要完成的工作。爲什麽不是呢?使用Collection類的一個優點是你可以通過添加更多的屬性和方法來擴展它的功能;不僅僅限于Add,Remove,Item,和Count。 在我們的Files類的情形中,它難道不對在類本身,而不是使用該類的每個應用程序裏面,放置掃描目錄的代碼做更多的檢測嗎?這是面向對象設計的一個指導原則:將代碼放在最靠近需要它的地方。 爲了闡述這個概念,我們向Collection類添加了一個Path屬性。當你設置這個屬性的時候,這個類將對目錄進行掃描,並向Collection對象添加它所找到的每個文件(見圖4)。 當一個過程改變該類的Path屬性的時候,將觸發PropertyLet過程。在我們的例子中,它發生在你從浏覽器對話框選擇了一個路徑之後。這裏是完成用文件列表填充Collection任務的簡單代碼。 'Reinitializethecollection. SetmobjFiles=NewFiles 'Setthepathproperty. mobjFiles.Path=strPath 一旦設置了Path屬性,Files類就將通俗化它自己的Collection,並且使它可以提供給應用程序。這已經是另一個例子了,在這個例子裏面,過程的"guts"存在于類自己內部(你可以證明類已不再需要它的Add和Remove方法了。在有些應用程序中,可能確實是這樣,但爲了達到說明的目的,我們已經選擇了留下它們作爲該類的一部分)。 一些不利之處 使用VBACollection類的生活並不總是美酒和玫瑰。當你使用Collection類代替Collection對象時,必須放棄兩樣東西。第一樣是Collection對象的默認方法,Item。默認的方法允許你從你的代碼中省略單詞"Item"。例如,如下兩條語句是一樣的,都是假定colFiles引用一個Collection對象。 Debug.PrintcolFiles.Item(1).Size Debug.PrintcolFiles(1).Size 除非你正在使用的是VisualBasic5.0,否則沒有辦法爲一個類指定默認的方法。因而,你必須總是顯式調用Item方法。 Collection類的第二個主要的不足之處是不能創建列舉函數。列舉類就是可以使ForEach循環工作的類。如果你想重複Collection中的每一個項目,那麽就必須用老式的方法來完成,也就是使用Count屬性和ForNext循環。例如,下面的代碼可以通俗化列表框: 'Fillthelistboxwithinfo. lstFiles.Clear ForlngCount=1TomobjFiles.Count WithmobjFiles.Item(lngCount) lstFiles.AddItem.ShortName&_ Space(12-Len(.ShortName))&_ vbTab&.AttributeString&_ vbTab&.Size EndWith Next 請注意該過程是如何使用計數器變量lngCount來完成從1到Collection中項目數的循環的。With語句使用了Item方法來引用Collection中的每個對象。 請注意,VisualBasic5.0用戶可以通過創建列舉函數來克服這一局限。在VisualBasicBookOnline中搜索關鍵字"enumeration"可以得到更爲詳細的信息。-
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 熱帖排行
 
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有