2012-01-26 23 views
13

請注意標籤:VBA,而不是VB6,而不是VB.NET。VBA中的自定義回調

這是特定於MS Access中的VBA。我在我稱之爲「Enumerable」的模塊中構建了一組方法。它做了很多讓人聯想到.NET中的Enumerable類和接口的事情。我想實現的一件事是ForEach方法,與.NET Enumerable.Select method類似。

我構建了一個使用Application.Run方法爲每個元素調用函數的版本,但Application.Run只能用於用戶定義的方法。例如,以下工作:

' User-defined wrapper function: 
Public Function MyReplace(_ 
    Expression As String, Find As String, StrReplace As String, _ 
    Optional Start As Long = 1, _ 
    Optional Count As Long = 1, _ 
    Optional Compare As VbCompareMethod = vbBinaryCompare) 
    MyReplace = Replace(Expression, Find, StrReplace, Start, Count, Compare) 
End Function 

' Using Application.Run to call a method by name 
Public Sub RunTest() 
    Debug.Print Run("MyReplace", "Input", "In", "Out") 
End Sub 

RunTest按預期打印「輸出」。以下不起作用:

Debug.Print Run("Replace", "Input", "In", "Out") 

它引發運行時錯誤430:「類不支持自動化或不支持預期的接口」。這是預期的,因爲該文檔指出Application.Run只適用於用戶定義的方法。

VBA確實有一個AddressOf運算符,但只適用於將函數指針傳遞給外部API函數的情況;使用AddressOf創建的函數指針在VBA中不可使用。同樣,這在文檔中有記載(或參見例如VBA - CallBacks = Few Cents Less Than A Dollar?)。

那麼有沒有其他的方法來識別和調用使用變量的方法?或者,我的回調 - ish嘗試將通過Application.Run方法限制爲用戶定義的函數嗎?

+4

你不會能夠使用addressof,但VBA支持類,所以基於接口的回調是好的; 'sub something(arg as string,myCallBack as IWhatever)... myCallBack.call(...)'將你的方法加入到一個類中,你可以用「CallByName」的名字來調用它們 - 由於處理它有點弱動態數量的參數,替代http://www.devx.com/tips/Tip/15422 –

+0

我有點困惑,你真的需要幫助。你的第一段談到實施'For ...每一個',但你的問題的其餘部分談論試圖在VBA函數上使用'Application.Run'。 – mischab1

+1

@ mischab1 - 查看任何.NET IEnumerable擴展方法。在他們中的大多數人中,您提供了一個回調來執行集合中的每個成員。我正在嘗試在VBA中做同樣的事情。 VBA沒有委託或函數指針或任何東西。我已經使用Application.Run作爲替代,但它的適用性有限。 –

回答

5

在一個星期...沒有其他的答案進行解析的緣故,這裏是我能想出的最好的:

  1. 我建立了一個解決一個ParamArray個別參數調用CallByName着想的輔助模塊。如果您將ParamArray傳遞給CallByName,它會將所有參數ma成單個實際Array,並將其傳遞給您試圖調用的方法中的第一個參數。
  2. 我建立了兩個ForEach方法:一個調用Application.Run,另一個調用CallByName。如問題所述,Application.Run僅適用於用戶定義的全局(公共模塊)方法。反過來,CallByName只適用於實例方法,並需要一個對象參數。

這仍然讓我無法通過名稱直接調用內置全局方法(例如Trim())。我對於解決辦法是建立用戶定義的包裝方法,只是調用內置的全球性的方法,例如:

Public Function FLeft(_ 
    str As String, _ 
    Length As Long) As String 
    FLeft = Left(str, Length) 
End Function 

Public Function FLTrim(_ 
    str As String) As String 
    FLTrim = LTrim(str) 
End Function 

Public Function FRight(_ 
    str As String, _ 
    Length As Long) As String 
    FRight = Right(str, Length) 
End Function 

...etc... 

我現在可以使用這些做這樣的事情:

' Trim all the strings in an array of strings 
trimmedArray = ForEachRun(rawArray, "FTrim") 

' Use RegExp to replace stuff in all the elements of an array 
' --> Remove periods that aren't between numbers 
Dim rx As New RegExp 
rx.Pattern = "(^|\D)\.(\D|$)" 
rx.Global = True 
resultArray = ForEachCallByName(inputArray, rx, "Replace", VbMethod, "$1 $2") 
+0

你的意思是,'FLTrim'在這裏'trimmedArray = ForEachRun(rawArray,「FTrim」)'? – bonCodigo

+1

@bonCodigo否,'Trim'和'LTrim'是獨立的VBA函數。在上面的代碼中,我展示了'LTrim'的封裝器的例子,下面的例子我使用'Trim'的封裝器。 –

+0

我有幾個替代建議,你是否仍然對這個問題的答案感興趣? – Blackhawk