CreateDelegate with unknown types
I came to similar to @kvb approach but with wrapper classes. They work a little faster because virtual methods are used instead of proxy actions. Also proxy action makes capturing context with invisible DisplayClass. This is my version.
using System;
using System.Reflection;
public abstract class DelegateWrapper
{
public static DelegateWrapper Create ( object target, MethodInfo method, Type arg ) =>
Create( target, method, typeof( DelegateWrapper<> ), arg );
public static DelegateWrapper Create ( object target, MethodInfo method, Type arg1, Type arg2 ) =>
Create( target, method, typeof( DelegateWrapper<,> ), arg1, arg2 );
private static DelegateWrapper Create ( object target, MethodInfo method, Type generic, params Type[] argTypes )
{
var wrapperType = generic.MakeGenericType( argTypes );
var ctor = wrapperType.GetConstructor( new Type[] { typeof( object ), typeof( MethodInfo ) } );
var wrapper = ctor.Invoke( new object[] { target, method } ) as DelegateWrapper;
return wrapper;
}
public virtual void Invoke ( object arg )
{
throw new NotSupportedException();
}
public virtual void Invoke ( object arg1, object arg2 )
{
throw new NotSupportedException();
}
}
public class DelegateWrapper<T> : DelegateWrapper
{
private Action<T> _delegate;
public DelegateWrapper ( object target, MethodInfo method )
{
_delegate = ( Action<T> )Delegate.CreateDelegate( typeof( Action<T> ), target, method );
}
public override void Invoke ( object arg )
{
_delegate.Invoke( ( T )arg );
}
}
public class DelegateWrapper<T1, T2> : DelegateWrapper
{
private Action<T1, T2> _delegate;
public DelegateWrapper ( object target, MethodInfo method )
{
_delegate = ( Action<T1, T2> )Delegate.CreateDelegate( typeof( Action<T1, T2> ), target, method );
}
public override void Invoke ( object arg1, object arg2 )
{
_delegate.Invoke( ( T1 )arg1, ( T2 )arg2 );
}
}
I implemented DelegateWrapper< T > and DelegateWrapper<T1, T2> but it is simple to extend the idea to implement any human reasonable number of arguments. It is required to implement corresponding Create() and Invoke() methods in base class and override only one suitable Invoke().
It can be used as the following:
object target;
var targetType = target.GetType();
var methodInfo = targetType.GetMethod("MethodWith2Strings");
var delegateWrapper = DelegateWrapper.Create(target, methodInfo, typeof(string), typeof(string));
delegateWrapper.Invoke("String 1", "String 2");
You invocation fails because You require object not a value type (like INT) -- obviously Func<T, int>
is not a Func<T, Int>
- it won't work with any vt like double or bool. Either return a boxed Int (or whatever vt You've got) . or (perhaps better) use reflection emit API.
By using reflection emit classes You can create dynamic methods and save them as delegates, or create Dynamic delegates and save them in some of Your structure. You could do this only once (perhaps once per runtime) store it in some Dict and invoke when needed.
hope it helps. luke
Here's one way to solve your problem. Create a generic method:
public static Func<T, object> MakeDelegate<U>(MethodInfo @get)
{
var f = (Func<T, U>)Delegate.CreateDelegate(typeof(Func<T, U>), @get);
return t => f(t);
}
This way, C#'s compiler takes care of inserting the necessary boxing (if any) to convert f(t)
(of type U
) to object
. Now you can use reflection to call this MakeDelegate
method with U
set to @get.ReturnType
, and what you get back will be a Func<T, object>
which can be called without needing to resort to using DynamicInvoke
.
Your original code can only work for reference types. That's why string wasn't a problem, it directly derives from System.Object. That a value type derives from ValueType and Object is a nice illusion on paper but actually requires code. The C# compiler automatically emits that code, it requires a boxing conversion. That's the part that is missing here, there is no runtime conversion from int to object without the BOX opcode.
You can get that opcode in your code, but you'll have to use System.Reflection.Emit.
Before you go there, first check if what you've got now is actually too slow. The expense of reflection is digging the metadata out of the assembly. That was done when you created the delegate, the type info is cached after that.