首页 > 解决方案 > CreateDelegate 错误:System.ArgumentException 无法绑定到目标方法

问题描述

我有一个应用程序获取外部应用程序的 dll,查看它以查找指定的类和方法。然后它methodinfo从这个外部方法中获取,然后尝试通过创建一个委托Delegate.CreateDelegate

我不断得到

System.ArgumentException: 'Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.'

我已经去提取了一些代码,以便更容易共享和调试,以及编写一个小的简单的外部应用程序来读取。请看下面的代码:

作为库的外部应用程序示例 (.Net Framework 4.8)

using System;

namespace MethodLib
{
    public class PrintText
    {
        public string Print(string textToPrint, int number)
        {
            return $"{ PrintPrivate(textToPrint) }: {number}";
        }

        public static string PrintStatic(string textToPrint)
        {
            return textToPrint;
        }

        public void PrintVoid(string textToPrint)
        {
            Console.WriteLine(textToPrint);
        }

        private string PrintPrivate(string textToPrint)
        {
            return $"This is { textToPrint }";
        }
    }
}

应用到 CreateDelegate

MethodInfo 创建

using System;
using System.Reflection;

namespace DelegateApp
{
    public class PluginSupport
    {
        public MethodInfo GetMethodInfo(string methodName, string externalLocation)
        {
            var instance = Activator.CreateInstance(Assembly.LoadFrom(externalLocation)
                .GetType("MethodLib.PrintText"));

            var methodInfo = instance.GetType()
                .GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);

            return methodInfo;
        }
    }
}

创建代表零件

namespace DelegateApp
{
    public class MethodGenerator
    {
        private PluginSupport _pluginSupport;

        public MethodGenerator()
        {
            _pluginSupport = new PluginSupport();
        }

        public MethodDetails Create(string methodName, string path)
        {
            var method = _pluginSupport.GetMethodInfo(methodName, path);

            if (Equals(method, null))
            {
                throw new KeyNotFoundException($"Method '{ methodName }' doesn't exist in class");
            }

            return new MethodDetails
            {
                MethodName = method.Name,
                ComponentName = method.DeclaringType.Name,
                FriendlyName = method.DeclaringType.Name,
                Parameters = method.GetParameters(),
                LogicalPath = method.DeclaringType.Assembly.Location,
                Method = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>), method)
            };
        }
    }
}

我试过什么

所以阅读了很多不同的帖子,我收集到我正在使用的电话

(Func<string>)Delegate.CreateDelegate(typeof(Func<string>), method)实际上仅适用于静态方法,并且由于我对所有公共方法感兴趣,因此我缺少目标/实例。

因此,从其他示例中,您需要创建实例并将其也传递进去,所以我使用了var myInstance = Actovator.CreateInstance,然后也传递了这个变量,最终得到以下结果

(Func<string>)Delegate.CreateDelegate(typeof(Func<string>), myInstance, method)

我也试过用这个

public static Delegate CreateDelegate(Type type, Type target, string method, bool ignoreCase);

这一切都在不断地抛出

System.ArgumentException: 'Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.'

我让它工作的唯一一次是当我执行以下操作时:

methodName = PrintStatic从外部应用程序

var methodInfo = instance.GetType()
                .GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);

var deleg = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>),null, method)

当然这不是我想要的,因为这只对我来说是静态的,我也想要非静态的。但即使这样,如果我添加BindingFlags.Instance到混合中,静态也会引发相同的错误。

如果我也删除BindingFlags.Instance和 my methodName = Print,则 methodInfo 为空。

我的问题

  1. 我不理解/缺少Delegate.CreateDelegate什么?
  2. 我错过了什么代码,这没有按我的预期工作?
  3. 有没有不同的方法来做同样的事情?
  4. 从创建委托开始,我想稍后在代码中调用它,但是直接在 methodinfo 上使用调用而不是创建委托然后调用它是否会受到惩罚?
  5. BindingFlags.Instance如果省略,为什么 methodinfo 不给我我的公共非静态成员?

标签: c#delegatesinvokeargumentexceptionmethodinfo

解决方案


感谢@Charlieface,我意识到我的签名类型与我创建委托不对应。

所以我最终在这个示例代码中结束的是在MethodGenerator课堂上执行以下操作

  1. parameters从方法信息中获取
  2. 遍历参数并将它们添加到类型列表中并获取每个参数的类型
  3. 构建一个func我不知道它需要的类型数量的地方,并用我从方法信息 + 输出类型中获得的参数数量替换这个数字
  4. 检查 if 方法isstatic并基于此将其设置为
methHead = method.IsStatic
                ? Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), null, method)
                : Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), instance, method);  

我猜这是一些复杂的代码,但它可以工作,并且需要对其进行改进或将其放入我们想要使用它的实际代码库中。但正如@Charlieface提到的if you don't know the type, there isn't much point to the delegate.

最后一段代码

 public MethodDetails Create(string methodName, string path)
        {
            var method = _pluginSupport.GetMethodInfo(methodName, path);

            if (Equals(method, null))
            {
                throw new KeyNotFoundException($"Method '{ methodName }' doesn't exist in class");
            }

            var instance = Activator.CreateInstance(method.DeclaringType);

            List<Type> types = new List<Type>();
            var methodPrams = method.GetParameters();
            
            foreach (var item in methodPrams)
            {
                types.Add(Type.GetType(item.ParameterType.UnderlyingSystemType.FullName));
            }

            var funcType = typeof(Func<>);

            var delegateFunc = Type.GetType(funcType.FullName.Replace("1", (methodPrams.Length + 1).ToString()));

            Delegate methHead;

                types.Add(typeof(string));

            methHead = method.IsStatic
                ? Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), null, method)
                : Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), instance, method);          

            return new MethodDetails
            {
                MethodName = method.Name,
                ComponentName = method.DeclaringType.Name,
                FriendlyName = method.DeclaringType.Name,
                Parameters = method.GetParameters(),
                LogicalPath = method.DeclaringType.Assembly.Location,
                Method = methHead
            };
        }

推荐阅读