【问题标题】:VB.Net: Call a module Method or Routine dynamically with parametersVB.Net:使用参数动态调用模块方法或例程
【发布时间】:2017-01-28 19:05:19
【问题描述】:

我想使用存储在 模块 中的变量名 运行一个方法,该方法带有一个参数:

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

我不想使用 Select Case,因为方法位于许多不同的模块中,我不想维护 select-case 函数。

我查了CallByName,但似乎这只适用于类。当涉及到 Modules 时,我不知道如何设置对象 ObjectRef

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

有没有办法在 VB.Net 中做到这一点?

编辑:为了让它变得非常简单,我需要相当于 VBA 的:

Application.Run module_name.sub_name param

【问题讨论】:

    标签: vb.net string methods module


    【解决方案1】:

    假设您想调用带有参数 123 的子例程(或函数)sub1,可选给定的模块名称 module1

    调用示例,如果模块名不可用(调用的函数名在项目中应该是唯一的):

    Dim FunctionName As String = "sub1"
    Dim Param As Integer = 123
    InvokeModuleFunction(FunctionNameToCall:=FunctionName, FunctionParameters:=Param)
    

    或者,如果您知道模块名称:

    Dim FunctionName As String = "sub1"
    Dim Param As Integer = 123    
    Dim ModuleName As String = "module1"
    InvokeModuleFunction(FunctionNameToCall:=FileType, ModuleName:=ModuleName, FunctionParameters:=Param)
    

    InvokeModuleFunction 定义

    Private Sub InvokeModuleFunction(FunctionNameToCall As String, FunctionParameters As Object, Optional ModuleName As String = Nothing)
        Dim MyReflectionAssembly = Reflection.Assembly.GetExecutingAssembly()
    
        Dim MyFunctionType As Type
        If IsNothing(ModuleName) Then
            'Gets function without ModuleName. FunctionName should be unique in the assembly/programm.
            MyFunctionType = MyReflectionAssembly.DefinedTypes.Where(Function(x) x.DeclaredMethods.Where(Function(y) y.Name = FunctionNameToCall).Count > 0).FirstOrDefault
        Else
            'Gets function using ModuleName, if available
            MyFunctionType = MyReflectionAssembly.DefinedTypes.Where(Function(x) x.Name = ModuleName AndAlso x.DeclaredMethods.Where(Function(y) y.Name = FunctionNameToCall).Count > 0).FirstOrDefault
        End If
    
        If Not IsNothing(MyFunctionType) Then MyFunctionType.GetMethod(FunctionNameToCall).Invoke(MyFunctionType, New Object() {FunctionParameters})
    End Sub
    

    或者,您可以在调用中使用多个参数。 您需要修改上述函数以允许传递多个参数。 调用部分如下所示:

    FunctionType.GetMethod(FunctionNameToCall).Invoke(FunctionType, New Object() {Par1, Par2, Par3})
    

    【讨论】:

    • 虽然这段代码 sn-p 可以解决问题,但它没有解释为什么或如何回答这个问题。请include an explanation for your code,因为这确实有助于提高您的帖子质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。您可以使用edit 按钮改进此答案以获得更多选票和声誉!
    【解决方案2】:

    您可以使用反射为Module 中的方法创建一个委托。我会将创建的委托加载到Dictionary(Of String, Action(Of Int32))

    Action(Of Int32) 被选中是因为它与您指定的带有整数参数的子例程的签名相匹配。

    假设你有一个这样定义的Module

    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
    

    现在创建代理并将其存储在字典中。

    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
    

    您可以像这样检索和调用委托:

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

    编辑:我有时会让事情变得复杂。 LoadMethods 方法可以在没有反射的情况下完成,如下所示:

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

    编辑 2:基于对下面问题和评论的编辑。

    编辑:为了让它变得非常简单,我需要相当于 VBA 的:

    Application.Run module_name.sub_name 参数

    您需要首先根据输入的名称从其包含的程序集中提取Module 类型。然后您可以检索MethodInfo,如上所示。以下示例假定模块包含在执行程序集中,并且实施了最少的检查。它将要求您提供模块名称、方法名称和正确键入的方法参数的数组。在现实世界的场景中,它可能需要获取参数字符串并执行某种类型的动态类型转换,以基于调用 MethodInfo.GetParameters 构建 typedArgs 数组。

    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
    

    用法示例:Exec("SomeModule", "Sub1", New Object() {123})

    【讨论】:

    • 谢谢,但这需要我将每个新的 Sub 和每个新模块添加到 LoadMethods(我不想维护)。我想要 VBA 的 VB.Net 等效项:Application.Run module_name.sub_name param
    • @BassemLhm,我提供了一个基本模板来完成您修改后的要求,作为对此答案的编辑。
    • 非常感谢。我唯一改变的是用 asm.GetName.Name 中的下划线替换空格 ...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-29
    • 2012-10-15
    • 2012-01-22
    • 2018-09-10
    • 2019-11-03
    • 2014-02-24
    • 1970-01-01
    相关资源
    最近更新 更多