2012-01-26 19 views
13

Lưu ý thẻ: VBA, không phải VB6, không phải VB.NET.Tùy chỉnh gọi lại trong VBA

Điều này dành riêng cho VBA trong MS Access. Tôi đã xây dựng một bộ sưu tập các phương thức trong một mô-đun mà tôi gọi là "Enumerable". Nó làm rất nhiều thứ gợi nhớ đến các lớp và giao diện Enumerable trong .NET. Một điều tôi muốn thực hiện là một phương pháp ForEach, analagous để các.NET Enumerable.Select method.

Tôi đã tạo phiên bản sử dụng phương thức Application.Run để gọi hàm cho từng phần tử, nhưng Application.Run chỉ hoạt động với các phương thức do người dùng xác định. Ví dụ: các công trình sau:

' 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 

Bản in RunTest "Đầu ra" như mong đợi. Sau đây KHÔNG hoạt động:

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

Nó ném lỗi thời gian chạy 430: "Lớp không hỗ trợ Tự động hóa hoặc không hỗ trợ giao diện dự kiến". Điều này được mong đợi, bởi vì tài liệu nói rằng Application.Run chỉ hoạt động cho các phương thức do người dùng định nghĩa.

VBA không có toán tử AddressOf, nhưng chỉ hoạt động khi chuyển các con trỏ hàm tới các hàm API bên ngoài; các con trỏ hàm được tạo bằng cách sử dụng AddressOf không thể tiêu hao trong VBA. Một lần nữa, điều này được ghi chú trong tài liệu (hoặc xem ví dụ VBA - CallBacks = Few Cents Less Than A Dollar?).

Vì vậy, có cách nào khác để xác định và gọi phương thức sử dụng biến không? Hoặc các nỗ lực gọi lại-ish của tôi có bị hạn chế đối với các hàm do người dùng định nghĩa thông qua phương thức Application.Run không?

+4

Bạn sẽ không có thể sử dụng addressof, nhưng VBA hỗ trợ các lớp để các callbacks dựa trên giao diện là tốt; 'sub something (arg as string, myCallBack as IWhatever) ... myCallBack.call (...)' Đưa các phương thức của bạn vào một lớp và bạn có thể gọi chúng bằng tên với "CallByName" - nó hơi yếu do xử lý số lượng đối số động, một thay thế http://www.devx.com/tips/Tip/15422 –

+0

Tôi hơi bối rối về những gì bạn thực sự cần trợ giúp. Đoạn đầu tiên của bạn nói về việc thực hiện 'Đối với ...Từng phần còn lại của câu hỏi của bạn nói về việc cố gắng sử dụng 'Application.Run' trên một hàm VBA. – mischab1

+1

@ mischab1 - Hãy xem bất kỳ phương pháp mở rộng .NET IEnumerable nào. Trong hầu hết chúng, bạn cung cấp một cuộc gọi lại để thực hiện trên mỗi thành viên của một bộ sưu tập. Tôi đang cố gắng làm điều tương tự trong VBA. VBA không có đại biểu hoặc con trỏ hàm hoặc bất kỳ thứ gì. Tôi đã sử dụng Application.Run như là một thay thế, nhưng nó có khả năng ứng dụng hạn chế. –

Trả lời

5

Không câu trả lời khác trong một tuần ... vì độ phân giải ở đây là tốt nhất mà tôi có thể đưa ra:

  1. tôi đã xây dựng một mô-đun helper mà giải quyết một ParamArray để lập luận cá nhân vì lợi ích của gọi CallByName. Nếu bạn vượt qua một ParamArray thông qua để CallByName nó sẽ nghiền tất cả các đối số thành một đơn, thực tế Array và chuyển cho đối số đầu tiên trong phương pháp bạn cố gắng gọi.
  2. Tôi đã xây dựng hai phương thức ForEach: một phương thức gọi Application.Run và một phương thức khác gọi số CallByName. Như đã lưu ý trong câu hỏi, Application.Run chỉ hoạt động cho các phương thức toàn cục (mô-đun công khai) do người dùng xác định. Đổi lại, CallByName chỉ hoạt động trên các phương thức mẫu và yêu cầu đối số đối tượng.

Điều đó vẫn khiến tôi không có cách nào gọi trực tiếp các phương thức toàn cục được tích hợp (chẳng hạn như Trim()) theo tên. Cách giải quyết của tôi cho điều đó là để xây dựng phương pháp wrapper người dùng định nghĩa mà chỉ gọi được xây dựng trong phương pháp toàn cầu, ví dụ:

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

tôi bây giờ có thể sử dụng chúng để làm những việc như:

' 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

Ý của bạn là 'FLTrim' ở đây' trimmedArray = ForEachRun (rawArray, "FTrim") '? – bonCodigo

+1

@bonCodigo No, 'Trim' và' LTrim' là các hàm VBA riêng biệt. Trong đoạn mã trên, tôi hiển thị ví dụ về trình bao bọc cho 'LTrim', và ví dụ dưới đây tôi sử dụng trình bao bọc cho' Trim'. –

+0

Tôi có một vài đề xuất thay thế, bạn vẫn muốn quan tâm đến câu trả lời cho câu hỏi? – Blackhawk

Các vấn đề liên quan