所以我试图创建一个不提前知道其目标类型的开放代表。我不确定这是否正确解释,让我告诉你:
class X
{
public bool test() { return false; }
}
static void Main()
{
var x = new X();
var runtimeType = x.GetType();
var method = runtimeType.GetMethod("test");
var del = ... INSERT CODE
Console.WriteLine(del(x)); // should output False
}
虽然Delegate.CreateDelegate(typeof(Func<X, bool>), method);
有效,但我在编译时不知道X
的类型。我想做的是使用typeof(Func<object, bool>)
,但这是不可能的。
我搜索并找到this文章。
我清理了一些代码 - 这是我的相关内容:
public static class MethodInfoExtensions
{
public static Func<TArg0, TReturn> F0<T, TArg0, TReturn>(MethodInfo method)
where T : TArg0
{
var d = (Func<T, TReturn>)Delegate.CreateDelegate(typeof(Func<T, TReturn>), method);
return delegate(TArg0 target) { return d((T)target); };
}
public static T DelegateForCallMethod<T>(this MethodInfo targetMethod)
{
//string creatorName = (targetMethod.ReturnType == typeof(void) ? "A" : "F") + targetMethod.GetParameters().Length.ToString();
// this will just do in my case
string creatorName = "F0";
var methodParams = targetMethod.GetParameters();
var typeGenArgs = typeof(T).GetGenericArguments();
var signature = new Type[1 + methodParams.Length + typeGenArgs.Length];
int idx = 0;
signature[idx++] = targetMethod.DeclaringType;
for (int i = 0; i < methodParams.Length; i++)
signature[idx++] = methodParams[i].ParameterType;
for (int i = 0; i < typeGenArgs.Length; i++)
signature[idx++] = typeGenArgs[i];
var mth = typeof(MethodInfoExtensions).GetMethod(creatorName, BindingFlags.NonPublic | BindingFlags.Static);
var gen = mth.MakeGenericMethod(signature);
var res = gen.Invoke(null, new object[] { targetMethod });
return (T)res;
}
}
现在我可以编写(在INSERT CODE区域中)method.DelegateForCallMethod<Func<object, bool>>();
,当我调用del(x)
时,它会执行x.test()
并正确输出False
!
问题是,将X
更改为struct
(这是我的实际用例)会破坏它! :(
Unhandled Exception: System.Reflection.TargetInvocationException: Exception has
been thrown by the target of an invocation. ---> System.ArgumentException: Error
binding to target method. at System.Delegate.CreateDelegate(Type type, MethodInfo method, Boolean throw
OnBindFailure) at Vexe.Runtime.Extensions.VexeTypeExtensions.F0[T,TArg0,TReturn](MethodInfo method) in c:\Users\vexe\Desktop\MyExtensionsAndHelpers\Source\Runtime\RuntimeExtensions\TypeExtensions.cs:line 24
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] argum
ents, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle
typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisib
ilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at Vexe.Runtime.Extensions.VexeTypeExtensions.DelegateForCallMethod[T](Method
Info targetMethod) in c:\Users\vexe\Desktop\MyExtensionsAndHelpers\Source\Runtime\RuntimeExtensions\TypeExtensions.cs:line 50
at Program.Main(String[] args) in c:\Users\vexe\Desktop\MyExtensionsAndHelpers\Solution\Test\Program2.cs:line 225
(该行为res = ...
)
知道为什么会这样吗?以及如何解决它?
谢谢!
编辑:我不想要使用MethodInfo.Invoke。这里的重点是创建一个委托,它比常规反射更快地调用。
修改:追踪问题,如果F0
是X
,struct
似乎无法创建代理 - 可以通过致电MethodInfoExtensions.F0<X, object, bool>(method);
- 如果X
是class
,则没问题!
修改简化甚至更多,如果Delegate.CreateDelegate(typeof(Func<X, bool>), method)
为X
,struct
似乎无法绑定!
编辑找到this - 几乎同样的问题。但是解决方案意味着在编译时使用参数类型(在我的情况下为X)中具有自定义委托:(
答案 0 :(得分:1)
问题是Delegate.CreateDelegate(typeof(Func<X, bool>), method)
如果X
是struct
则失败 - 根据this我应该创建自己的委托并通过ref传递。我做到了,它有效,但现在如果我改回class
则不行!如果我删除了class
,它会再次开始为struct
而不是ref
开始工作!
所以给出了这个启动代码:
class X
{
public bool test() { return false; }
}
var x = new X();
var runtimeType = x.GetType();
var method = runtimeType.GetMethod("test");
Case1(如果X
是班级,则有效)
delegate TReturn MyDelegate1<TArg0, TReturn>(TArg0 obj);
var del = Delegate.CreateDelegate(typeof(MyDelegate1<X, bool>), method) as MyDelegate1<X, bool>;
Console.WriteLine(del(x));
Case2(如果X
是struct,则有效)
delegate TReturn MyDelegate2<TArg0, TReturn>(ref TArg0 obj);
var del = Delegate.CreateDelegate(typeof(MyDelegate2<X, bool>), method) as MyDelegate2<X, bool>;
Console.WriteLine(del(ref x));
现在为了使原始代码适应这种情况,我必须有两个代表版本:一个用ref,另一个没有。在DelegateForCallMethod
函数中,我看到输入方法的DeclaringType
是结构还是类,并相应地使用相应的委托类型(我甚至不确定它是否有效)
如果有效,可能会更新以添加代码。
如果有人能解释发生了什么,请欣赏它。
编辑:我们走了 - (绝对不是最漂亮的 - 我觉得我在做一些多余的事情):
public delegate TReturn MethodInvoker<TArg0, TReturn>(TArg0 target);
public delegate TReturn MethodInvokerRef<TArg0, TReturn>(ref TArg0 target);
public static MethodInvoker<TArg0, TReturn> F0Class<T, TArg0, TReturn>(MethodInfo method)
where T : TArg0
{
var d = Delegate.CreateDelegate(typeof(MethodInvoker<T, TReturn>), method) as MethodInvoker<T, TReturn>;
return delegate(TArg0 target)
{
return d((T)target);
};
}
public static MethodInvokerRef<TArg0, TReturn> F0Struct<T, TArg0, TReturn>(MethodInfo method)
where T : TArg0
{
var d = Delegate.CreateDelegate(typeof(MethodInvokerRef<T, TReturn>), method) as MethodInvokerRef<T, TReturn>;
return delegate(ref TArg0 target)
{
var typed = (T)target;
return d(ref typed);
};
}
public static Func<TArg0, TReturn> DelegateForCallMethod<TArg0, TReturn>(this MethodInfo targetMethod)
{
var declType = targetMethod.DeclaringType;
var signature = new Type[3]
{
declType,
typeof(TArg0),
typeof(TReturn)
};
bool isValueType = declType.IsValueType;
string delegateCreator;
if (isValueType)
delegateCreator = "F0Struct";
else
delegateCreator = "F0Class";
var mth = typeof(VexeTypeExtensions).GetMethod(delegateCreator, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
var gen = mth.MakeGenericMethod(signature);
var res = gen.Invoke(null, new object[] { targetMethod });
if (isValueType)
{
var mir = (MethodInvokerRef<TArg, TReturn>)res;
return x => mir(ref x);
}
var mi = (MethodInvoker<TArg, TReturn>)res;
return x => mi(x);
}
用法:
var x = // ... usual startup code
var del = method.DelegateForCallMethod<object, bool>();
Console.WriteLine(del(x));
答案 1 :(得分:0)
在这一行:
var mth = typeof(MethodInfoExtensions).GetMethod(creatorName, BindingFlags.NonPublic | BindingFlags.Static);
使标志适合您的方法
答案 2 :(得分:0)
我正在使用类似的代码来创建未知实例类型的getter和setter。
它使用MakeGenericType
代替MakeGenericMethod
,这样可以摆脱GetMethod
和Invoke
,转而使用Activator.CreateInstance
。
using System;
using System.Reflection;
public static class GetterSetterHelper
{
abstract class Factory<T, TValue>
{
public abstract Func<T, TValue> CreateGetter(MethodInfo method);
public abstract Action<T, TValue> CreateSetter(MethodInfo method);
public static Factory<T, TValue> Create(Type runtimeType)
{
var genericType = runtimeType.IsValueType ? typeof(StructFactory<,,>) : typeof(Factory<,,>);
var factoryType = genericType.MakeGenericType(new Type[] { runtimeType, typeof(T), typeof(TValue) });
return (Factory<T, TValue>)Activator.CreateInstance(factoryType);
}
}
class Factory<TRuntime, T, TValue> : Factory<T, TValue>
where TRuntime : class, T
{
public override Func<T, TValue> CreateGetter(MethodInfo method)
{
var d = (Func<TRuntime, TValue>)Delegate.CreateDelegate(typeof(Func<TRuntime, TValue>), method);
return delegate (T target) { return d((TRuntime)target); };
}
public override Action<T, TValue> CreateSetter(MethodInfo method)
{
var d = (Action<TRuntime, TValue>)Delegate.CreateDelegate(typeof(Action<TRuntime, TValue>), method);
return delegate (T target, TValue value) { d((TRuntime)target, value); };
}
}
class StructFactory<TRuntime, T, TValue> : Factory<T, TValue>
where TRuntime : struct, T
{
delegate TValue GetterDelegate(ref TRuntime instance);
public override Func<T, TValue> CreateGetter(MethodInfo method)
{
var d = (GetterDelegate)Delegate.CreateDelegate(typeof(GetterDelegate), method);
return delegate (T target)
{
var inst = (TRuntime)target;
return d(ref inst);
};
}
public override Action<T, TValue> CreateSetter(MethodInfo method)
{
// It makes little sense to create setter which sets value to COPY of value type
// It would make sense if we use delegate like:
// void ActionRef<T, TValue(ref T inst, TValue value);
throw new NotSupportedException();
}
}
public static Func<T, TValue> CreateGetter<T, TValue>(this MethodInfo methodInfo)
{
return Factory<T, TValue>.Create(methodInfo.ReflectedType).CreateGetter(methodInfo);
}
public static Action<T, TValue> CreateSetter<T, TValue>(this MethodInfo methodInfo)
{
return Factory<T, TValue>.Create(methodInfo.ReflectedType).CreateSetter(methodInfo);
}
}
测试代码:
using System;
class Program
{
class Test
{
public int DoSomething() { return 1; }
}
struct TestStruct
{
public int DoSomething() { return 2; }
}
static void Main(string[] args)
{
var method = typeof(Test).GetMethod("DoSomething");
var getter = method.CreateGetter<object, int>();
Console.WriteLine(getter(new Test()));
var method2 = typeof(TestStruct).GetMethod("DoSomething");
var getter2 = method2.CreateGetter<object, int>();
Console.WriteLine(getter2(new TestStruct()));
Console.ReadKey();
}
}