【问题标题】:Reflection: How to Invoke Method with parameters反射:如何使用参数调用方法
【发布时间】:2011-01-13 05:29:11
【问题描述】:

我试图通过带参数的反射调用一个方法,我得到:

对象与目标类型不匹配

如果我调用不带参数的方法,它可以正常工作。基于以下代码,如果我调用方法Test("TestNoParameters"),它可以正常工作。但是,如果我打电话给Test("Run"),我会遇到异常。我的代码有问题吗?

我最初的目的是传递一个对象数组,例如public void Run(object[] options) 但这不起作用,我尝试了一些更简单的方法,例如字符串没有成功。

// Assembly1.dll
namespace TestAssembly
{
    public class Main
    {
        public void Run(string parameters)
        { 
            // Do something... 
        }
        public void TestNoParameters()
        {
            // Do something... 
        }
    }
}

// Executing Assembly.exe
public class TestReflection
{
    public void Test(string methodName)
    {
        Assembly assembly = Assembly.LoadFile("...Assembly1.dll");
        Type type = assembly.GetType("TestAssembly.Main");

        if (type != null)
        {
            MethodInfo methodInfo = type.GetMethod(methodName);

            if (methodInfo != null)
            {
                object result = null;
                ParameterInfo[] parameters = methodInfo.GetParameters();
                object classInstance = Activator.CreateInstance(type, null);

                if (parameters.Length == 0)
                {
                    // This works fine
                    result = methodInfo.Invoke(classInstance, null);
                }
                else
                {
                    object[] parametersArray = new object[] { "Hello" };

                    // The invoke does NOT work;
                    // it throws "Object does not match target type"             
                    result = methodInfo.Invoke(methodInfo, parametersArray);
                }
            }
        }
    }
}

【问题讨论】:

  • 正确的行是 object[] parametersArray = new object[] { new object[] { "Hello" } };
  • 如果你知道有一个类型总是先加载,你可以在上面放一个静态ctor。

标签: c# reflection parameters methods invoke


【解决方案1】:

我发布这个答案是因为很多访问者从谷歌进入这里来解决这个问题。


string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );

当外部 .dll - 而不是 this.GetType() 时,您可以使用 typeof(YourClass)

【讨论】:

    【解决方案2】:

    在 .Net 4.7.2 上调用从外部程序集加载的类中的方法,您可以在 VB.net 中使用以下代码

            Dim assembly As Reflection.Assembly = Nothing
            Try
                assembly = Reflection.Assembly.LoadFile(basePath & AssemblyFileName)
                Dim typeIni = assembly.[GetType](AssemblyNameSpace & "." & "nameOfClass")
                Dim iniClass = Activator.CreateInstance(typeIni, True)
                Dim methodInfo = typeIni.GetMethod("nameOfMethod")
    
                'replace nothing by a parameter array if you need to pass var. paramenters
                Dim parametersArray As Object() = New Object() {...}
                'without parameters is like this
                Dim result = methodInfo.Invoke(iniClass, Nothing)
            Catch ex As Exception
                MsgBox("Error initializing main layout:" & ex.Message)
                Application.Exit()
                Exit Sub
            End Try
    

    【讨论】:

      【解决方案3】:

      我正在通过反射调用加权平均值。并且使用了多个参数的方法。

      Class cls = Class.forName(propFile.getProperty(formulaTyp));// reading class name from file
      
      Object weightedobj = cls.newInstance(); // invoke empty constructor
      
      Class<?>[] paramTypes = { String.class, BigDecimal[].class, BigDecimal[].class }; // 3 parameter having first is method name and other two are values and their weight
      Method printDogMethod = weightedobj.getClass().getMethod("applyFormula", paramTypes); // created the object 
      return BigDecimal.valueOf((Double) printDogMethod.invoke(weightedobj, formulaTyp, decimalnumber, weight)); calling the method
      

      【讨论】:

        【解决方案4】:

        我尝试使用上述所有建议的答案,但似乎对我没有任何用处。所以我想在这里解释什么对我有用。

        我相信,如果您正在调用下面的 Main 之类的方法,或者甚至在您的问题中使用单个参数,您只需将参数类型从 string 更改为 object 即可。我有一个像下面这样的课程

        //Assembly.dll
        namespace TestAssembly{
            public class Main{
        
                public void Hello()
                { 
                    var name = Console.ReadLine();
                    Console.WriteLine("Hello() called");
                    Console.WriteLine("Hello" + name + " at " + DateTime.Now);
                }
        
                public void Run(string parameters)
                { 
                    Console.WriteLine("Run() called");
                    Console.Write("You typed:"  + parameters);
                }
        
                public string TestNoParameters()
                {
                    Console.WriteLine("TestNoParameters() called");
                    return ("TestNoParameters() called");
                }
        
                public void Execute(object[] parameters)
                { 
                    Console.WriteLine("Execute() called");
                   Console.WriteLine("Number of parameters received: "  + parameters.Length);
        
                   for(int i=0;i<parameters.Length;i++){
                       Console.WriteLine(parameters[i]);
                   }
                }
        
            }
        }
        

        然后,您必须在调用它时将 parameterArray 传递到如下所示的对象数组中。下面的方法是你需要工作的

        private void ExecuteWithReflection(string methodName,object parameterObject = null)
        {
            Assembly assembly = Assembly.LoadFile("Assembly.dll");
            Type typeInstance = assembly.GetType("TestAssembly.Main");
        
            if (typeInstance != null)
            {
                MethodInfo methodInfo = typeInstance.GetMethod(methodName);
                ParameterInfo[] parameterInfo = methodInfo.GetParameters();
                object classInstance = Activator.CreateInstance(typeInstance, null);
        
                if (parameterInfo.Length == 0)
                {
                    // there is no parameter we can call with 'null'
                    var result = methodInfo.Invoke(classInstance, null);
                }
                else
                {
                    var result = methodInfo.Invoke(classInstance,new object[] { parameterObject } );
                }
            }
        }
        

        该方法方便调用方法,可以如下调用

        ExecuteWithReflection("Hello");
        ExecuteWithReflection("Run","Vinod");
        ExecuteWithReflection("TestNoParameters");
        ExecuteWithReflection("Execute",new object[]{"Vinod","Srivastav"});
        

        【讨论】:

          【解决方案5】:
           Assembly assembly = Assembly.LoadFile(@"....bin\Debug\TestCases.dll");
                 //get all types
                  var testTypes = from t in assembly.GetTypes()
                                  let attributes = t.GetCustomAttributes(typeof(NUnit.Framework.TestFixtureAttribute), true)
                                  where attributes != null && attributes.Length > 0
                                  orderby t.Name
                                  select t;
          
                  foreach (var type in testTypes)
                  {
                      //get test method in types.
                      var testMethods = from m in type.GetMethods()
                                        let attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true)
                                        where attributes != null && attributes.Length > 0
                                        orderby m.Name
                                        select m;
          
                      foreach (var method in testMethods)
                      {
                          MethodInfo methodInfo = type.GetMethod(method.Name);
          
                          if (methodInfo != null)
                          {
                              object result = null;
                              ParameterInfo[] parameters = methodInfo.GetParameters();
                              object classInstance = Activator.CreateInstance(type, null);
          
                              if (parameters.Length == 0)
                              {
                                  // This works fine
                                  result = methodInfo.Invoke(classInstance, null);
                              }
                              else
                              {
                                  object[] parametersArray = new object[] { "Hello" };
          
                                  // The invoke does NOT work;
                                  // it throws "Object does not match target type"             
                                  result = methodInfo.Invoke(classInstance, parametersArray);
                              }
                          }
          
                      }
                  }
          

          【讨论】:

            【解决方案6】:

            我会这样使用它,它的方式更短,不会有任何问题

                    dynamic result = null;
                    if (methodInfo != null)
                    {
                        ParameterInfo[] parameters = methodInfo.GetParameters();
                        object classInstance = Activator.CreateInstance(type, null);
                        result = methodInfo.Invoke(classInstance, parameters.Length == 0 ? null : parametersArray);
                    }
            

            【讨论】:

              【解决方案7】:

              提供的解决方案不适用于从远程程序集加载的类型实例。为此,这里有一个适用于所有情况的解决方案,它涉及对通过 CreateInstance 调用返回的类型进行显式类型重新映射。

              这就是我需要创建类实例的方式,因为它位于远程程序集中。

              // sample of my CreateInstance call with an explicit assembly reference
              object classInstance = Activator.CreateInstance(assemblyName, type.FullName); 
              

              但是,即使使用上面提供的答案,您仍然会遇到相同的错误。方法如下:

              // first, create a handle instead of the actual object
              ObjectHandle classInstanceHandle = Activator.CreateInstance(assemblyName, type.FullName);
              // unwrap the real slim-shady
              object classInstance = classInstanceHandle.Unwrap(); 
              // re-map the type to that of the object we retrieved
              type = classInstace.GetType(); 
              

              然后按照此处提到的其他用户进行操作。

              【讨论】:

                【解决方案8】:

                你那里有一个错误

                result = methodInfo.Invoke(methodInfo, parametersArray);
                

                应该是

                result = methodInfo.Invoke(classInstance, parametersArray);
                

                【讨论】:

                  【解决方案9】:

                  这里有一个根本性的错误:

                  result = methodInfo.Invoke(methodInfo, parametersArray); 
                  

                  您正在MethodInfo 的实例上调用该方法。您需要传入要调用的对象类型的实例。

                  result = methodInfo.Invoke(classInstance, parametersArray);
                  

                  【讨论】:

                    【解决方案10】:

                    将“methodInfo”更改为“classInstance”,就像在使用空参数数组的调用中一样。

                      result = methodInfo.Invoke(classInstance, parametersArray);
                    

                    【讨论】:

                    • 这有效,除非使用远程程序集的实例。问题是它溢出了同样的错误,这不是很有帮助。我花了几个小时试图修复它,并为我的案例和此处提供的案例发布了一个新的通用解决方案。万一有人需要它:)
                    • 如果参数是多种类型的,那么数组应该是怎样的呢?对象数组??
                    • 是的,如果参数中有多种类型,它应该是一个对象[]
                    猜你喜欢
                    • 2012-06-16
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-02-18
                    相关资源
                    最近更新 更多