dimanche 1 février 2015

Open instance delegate with unknown target type?

So I'm trying to create an open delegate that doesn't know the type of its target in advance. I'm not sure if that explains it correctly, let me show you:



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
}


While Delegate.CreateDelegate(typeof(Func<X, bool>), method); works, but I don't know the type of X at compile time. What I'd like to do, is use typeof(Func<object, bool>) but that's not possible.


I searched and found this article.


I cleaned up some of the code - here's the related bit for me:



public static class MethodInfoExtensions
{
private 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;

var mParams = targetMethod.GetParameters();
for (int i = 0; i < mParams.Length; i++)
signature[idx++] = mParams[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;
}
}


Now I can write (in INSERT CODE area) method.DelegateForCallMethod<Func<object, bool>>(); and when I call del(x) it would execute x.test() and output False correctly!


The problem is, changing X to be a struct (which is my actual use-case) breaks it! :(



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


Any idea why this happens? and how to fix it?


Thanks!






Aucun commentaire:

Enregistrer un commentaire