2012-01-26 17 views
13

Uwaga na tag: VBA, a nie VB6, a nie VB.NET.Niestandardowe wywołania zwrotne w języku VBA

Jest to specyficzne dla VBA w MS Access. Zbudowałem kolekcję metod w module, który nazywam "Enumerable". Ma wiele rzeczy przypominających klasy i interfejsy Enumerable w .NET. Jedną z rzeczy, którą chcę zaimplementować, jest metoda ForEach, analagiczna dla .NET Enumerable.Select method.

Zbudowałem wersję, która używa metody Application.Run do wywołania funkcji dla każdego elementu, ale Application.Run działa tylko z metodami zdefiniowanymi przez użytkownika. Na przykład następujące prace:

' 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 

Funkcja RunTest drukuje "Wyjście" zgodnie z oczekiwaniami. Poniższa nie działa:

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

To wyrzuca błąd run-time 430: „Klasa nie obsługuje automatyzacji lub nie obsługuje oczekiwanego interfejsu”. Jest to oczekiwane, ponieważ dokumentacja stwierdza, że ​​Application.Run działa tylko dla metod zdefiniowanych przez użytkownika.

VBA ma operatora AddressOf, ale działa to tylko przy przekazywaniu wskaźników funkcji do zewnętrznych funkcji API; wskaźniki funkcyjne utworzone za pomocą AddressOf nie są przydatne w VBA. Ponownie, jest to odnotowane w dokumentacji (lub zobacz na przykład VBA - CallBacks = Few Cents Less Than A Dollar?).

Czy istnieje inny sposób identyfikacji i wywołania metody przy użyciu zmiennej? Czy moje próby wywołania zwrotnego będą ograniczone do funkcji zdefiniowanych przez użytkownika za pomocą metody Application.Run?

+4

wont być w stanie używać adresu, ale VBA obsługuje klasy, więc wywołania zwrotne oparte na interfejsie są w porządku; 'sub coś (arg as string, myCallBack jak IWhatever) ... myCallBack.call (...)' Wypchaj swoje metody do klasy i możesz wywoływać je po nazwie za pomocą "CallByName" - jest to trochę słabe ze względu na obsługę dynamicznej liczby argumentów, alternatywa http://www.devx.com/tips/Tip/15422 –

+0

Jestem trochę zdezorientowany tym, z czym naprawdę potrzebujesz pomocy. Twój pierwszy akapit mówi o implementacji 'For ...Każda z nich, ale reszta twojego pytania mówi o próbie użycia 'Application.Run' na funkcji VBA. – mischab1

+1

@ mischab1 - Spójrz na dowolne metody rozszerzeń .NET IEnumerable. W większości z nich zapewniasz wywołanie zwrotne do wykonania dla każdego członka kolekcji. Próbuję zrobić to samo w VBA. VBA nie ma delegatów ani wskaźników funkcji, ani niczego. Użyłem Application.Run jako substytutu, ale ma ograniczone zastosowanie. –

Odpowiedz

5

Żadne inne odpowiedzi w tygodniu ... Dla dobra rozdzielczość jest tutaj najlepszym mogłem wymyślić:

  1. zbudowałem moduł pomocnika, który rozwiązuje ParamArray do poszczególnych argumentów na rzecz zwołania CallByName. Jeśli przełączysz parametr ParamArray do CallByName, zacieśni on wszystkie argumenty do pojedynczego, rzeczywistego Array i przekaże je do pierwszego argumentu w metodzie, którą próbujesz wywołać.
  2. Zbudowałem dwie metody ForEach: jedną, która wywołuje Application.Run, i drugą, która wywołuje CallByName. Jak wspomniano w pytaniu, Application.Run działa tylko dla zdefiniowanych przez użytkownika metod globalnego (modułu publicznego). Z kolei CallByName działa tylko na metodach instancji i wymaga argumentu obiektu.

To nadal pozostawia mnie bez możliwości bezpośredniego wywoływania wbudowanych metod globalnych (takich jak Trim()) według nazwy. Moje obejście że jest zbudowanie metody otoki zdefiniowane przez użytkownika, że ​​wystarczy zadzwonić wbudowanej metody globalnej, na przykład:

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... 

mogę teraz wykorzystać je do robienia rzeczy, jak:

' 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

Masz na myśli 'FLTrim' tutaj' trimmedArray = ForEachRun (rawArray, "FTrim") '? – bonCodigo

+1

@bonCodigo Nie, 'Trim' i' LTrim' są oddzielnymi funkcjami VBA. W górnym kodzie pokazuję przykład opakowania dla 'LTrim', a dolny przykład używam opakowania dla' Trim'. –

+0

Mam kilka alternatywnych propozycji, czy nadal byłbyś zainteresowany odpowiedziami na pytanie? – Blackhawk

Powiązane problemy