我采用了类似于@kvb 的方法,但使用了包装类。它们工作得更快一些,因为使用了虚拟方法而不是代理操作。代理动作也使用不可见的 DisplayClass 捕获上下文。这是我的版本。
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 );
}
}
我实现了 DelegateWrapper 和 DelegateWrapper 但很容易扩展这个想法来实现任何人类合理数量的参数。需要在基类中实现相应的 Create() 和 Invoke() 方法,并且只覆盖一个合适的 Invoke()。
可以如下使用:
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");