Bassem Lhm Bassem Lhm - 2 months ago 12
Vb.net Question

VB.Net: Call a module Method or Routine dynamically with parameters

I want to run a Method using a variable name that is stored in a Module with a parameter:

Dim subName as String = "sub1"
Dim param as Integer = 123
sub1(param) <- I want to run this using the **subName** string


I don't want to use
Select
Case
because the Methods are in many different modules and I don't want to maintain a select-case function.

I looked up
CallByName
but it seems this only works for Classes. I can't figure out how to set the object
ObjectRef
when it comes to Modules :

Public Function CallByName(ByVal ObjectRef As System.Object,ByVal ProcName As String,ByVal UseCallType As CallType, ByVal Args() As Object) As Object


Is there a way to do this in VB.Net?

Edit: To make it really simple, I need the equivalent of VBA's:

Application.Run module_name.sub_name param

Answer

You can use reflection to create a delegate to the methods in the Module. I would load the created delegates into a Dictionary(Of String, Action(Of Int32)).

Action(Of Int32) is chosen because it matches the signature you specified of a subroutine taking an integer parameter.

Assume you have a Module defined like this:

Module SomeModule
    Public Sub Sub1(arg As Int32)
        Console.WriteLine("Sub1: {0}", arg)
    End Sub
    Public Sub Sub2(arg As Int32)
        Console.WriteLine("Sub2: {0}", arg)
    End Sub
End Module

Now to create and store the delegates in a dictionary.

Private methods As New Dictionary(Of String, Action(Of Int32))
Sub LoadMethods()
    Dim modType As Type = GetType(SomeModule)
    Dim mi As Reflection.MethodInfo
    mi = modType.GetMethod("Sub1", BindingFlags.Static Or BindingFlags.Public)
    methods.Add(mi.Name, CType(mi.CreateDelegate(GetType(Action(Of Int32))), Action(Of Int32)))

    mi = modType.GetMethod("Sub2", BindingFlags.Static Or BindingFlags.Public)
    methods.Add(mi.Name, CType(mi.CreateDelegate(GetType(Action(Of Int32))), Action(Of Int32)))
End Sub

You can retrieve and invoke the delegate like this:

methods("Sub1")(123)
methods("Sub2")(456)

Edit: I sometimes makes things to complicated. The LoadMethods method can be done without reflection like this:

Sub LoadMethods()
    methods.Add("Sub1", New Action(Of Int32)(AddressOf SomeModule.Sub1))
    methods.Add("Sub2", New Action(Of Int32)(AddressOf SomeModule.Sub1))
End Sub

Edit 2: Based on edit to question and comment below.

Edit: To make it really simple, I need the equivalent of VBA's:

Application.Run module_name.sub_name param

You will need to first extract the Module type from its containing assembly based on the entered name. Then you can retrieve the MethodInfo as shown above. The following example assumes that the module is contained in the executing assembly and has minimal checks implemented. It will require you to provide the module name, method name and an array properly typed method arguments. In a real world scenario, it would probably need to take a string of the arguments and perform some type of dynamic type casting to build up the typedArgs array based on calling MethodInfo.GetParameters.

Private Shared Sub Exec(moduleName As String, methodName As String, typedArgs As Object())
    Dim asm As Reflection.Assembly = Assembly.GetExecutingAssembly
    Dim modType As Type = asm.GetType(String.Format("{0}.{1}", asm.GetName.Name, moduleName))
    If modType IsNot Nothing Then
        Dim mi As Reflection.MethodInfo
        mi = modType.GetMethod(methodName, BindingFlags.Static Or BindingFlags.Public)
        If mi IsNot Nothing Then
            mi.Invoke(Nothing, typedArgs)
        End If
    End If
End Sub

Example usage: Exec("SomeModule", "Sub1", New Object() {123})