CreateDelegate Error: System.ArgumentException Cannot bind to the target method

CreateDelegate Error: System.ArgumentException Cannot bind to the target method

我有一个使用外部应用程序 dll 的应用程序,查看它以查找指定的 class 和方法。然后它从这个外部方法获取 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 }";
        }
    }
}

App 到 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)
            };
        }
    }
}

我尝试了什么

所以阅读了很多不同的 post 我知道我正在使用的电话

(Func<string>)Delegate.CreateDelegate(typeof(Func<string>), method) 实际上仅适用于静态方法,因为我对所有 public 方法都感兴趣,所以我缺少 target/instance.

因此从其他示例中,您需要创建实例并将其也传递进来,所以我使用了 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 和我的 methodName = Print,则 methodInfo 为空。

我的问题

  1. 关于 Delegate.CreateDelegate,我不是什么 understanding/missing?
  2. 我错过了什么代码,它没有像我预期的那样工作?
  3. 是否有不同的方法来做同样的事情?
  4. 从创建委托开始,我想稍后在代码中调用它,但是直接在 methodinfo 上使用调用而不是创建委托然后调用它是否有惩罚?
  5. 如果 BindingFlags.Instance 被省略,为什么 methodinfo 不给我我的 public 非静态成员?

感谢 @Charlieface,我意识到我的签名类型与我创建委托的方式不一致。

所以我最终在此示例代码中完成的是在 MethodGenerator class

中执行以下操作
  1. 从 methodinfo
  2. 获取 parameters
  3. 遍历参数并将它们添加到类型列表中并获取每个参数的类型
  4. 构建一个 func,我不知道它需要多少类型,并将该数字替换为我从 methodinfo + 输出类型
  5. 获得的参数数量
  6. 检查是否方法 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
            };
        }