问题描述
我有一个应用程序,它接受外部应用程序的 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
和我的 methodName = Print
,则 methodInfo 为空。
我的问题
- 关于
Delegate.CreateDelegate
,我有什么不理解/遗漏的地方? - 我错过了什么代码,这不能按我的预期工作?
- 做同样的事情有没有不同的方式?
- 从创建委托开始,我想稍后在代码中调用它,但是直接在 methodinfo 上使用调用而不是创建委托然后调用它是否会受到惩罚?
- 如果省略
BindingFlags.Instance
,为什么 methodinfo 不给我我的公共非静态成员?
解决方法
感谢 @Charlieface,我意识到我的签名类型与我创建的委托并不对应。
所以我最终在这个示例代码中得到的是在 MethodGenerator
类中执行以下操作
- 从 methodinfo 中获取
parameters
- 遍历参数并将它们添加到类型列表中并获取每个参数的类型
- 构建一个
func
,其中我不知道它需要的类型数量,并用我从 methodinfo + 输出类型获得的参数数量替换该数量 - 检查方法
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()),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
};
}