加速Reflection Invoke C#/ .NET

Rob*_*Rob 15 .net c# reflection invoke

有很多关于加速反射调用的帖子,例如:

使用.NET/C中的委托加速Reflection API

https://codeblog.jonskeet.uk/2008/08/09/making-reflection-fly-and-exploring-delegates/

和这里:

示例:使用.NET/C#中的委托加速Reflection API



我的问题是关于加速泛型调用.这有可能吗?

我有一个抽象类和一个实现它的类......

public abstract class EncasulatedMessageHandler<T> where T : Message
{
    public abstract void HandleMessage(T message);
}

public class Handler : EncasulatedMessageHandler<MyMessageType>
{
    public int blat = 0;
    public override void HandleMessage(MyMessageType message) { blat++; }
}
Run Code Online (Sandbox Code Playgroud)

我想要做的是建立这些消息处理程序类的列表并快速调用它们的HandleMessage()


目前,我正在做的事情大致如下:

object handler = Activator.CreateInstance(typeof(Handler)); // Ignore this, this is done up front.

MethodInfo method = type.GetMethod("HandleMessage", BindingFlags.Instance | BindingFlags.Public);

Action<object> hook = new Action<object>(delegate(object message)
{
    method.Invoke(handler, new object[] { message });
});

// Then when I want to invoke it:

hook(new MyMessageType());
Run Code Online (Sandbox Code Playgroud)

这不是全部,但它是重要的东西......

方法.Invoke非常慢,我想在类上保留泛型参数,我意识到我可以将它锁定到对象并将其转换为HandleMessage方法,但我试图避免这样做.

有什么办法可以加快速度吗?它目前比直接呼叫慢几个数量级.

任何帮助,将不胜感激.

Tim*_*ers 8

使用Delegate.CreateDelegate()应该快得多.最终会得到一个指向真实函数的指针,而不是一个调用的委托Invoke().

试试这个:

object handler = Activator.CreateInstance(typeof(Handler)); 
var handlerType = handler.GetType();
var method = handlerType.GetMethod("HandleMessage", BindingFlags.Instance | BindingFlags.Public);
var paramType = handlerType.GetGenericArguments()[0];

// invoke the MakeHandleMessageDelegate method dynamically with paramType as the type parameter
// NB we're only doing this once
Action<object> hook = (Action<object>) this.GetType().GetMethod("MakeHandleMessageDelegate")
            .MakeGenericMethod(paramType)
            .Invoke(null, new [] { handler });
Run Code Online (Sandbox Code Playgroud)

在同一个类中添加以下泛型方法.我们在上面动态调用它,因为我们在编译时不知道类型参数.

public static Action<object> MakeHandleMessageDelegate<T>(object target)
{
    var d = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), target, "HandleMessage");

    // wrap the delegate another that simply casts the object parameter to the required type
    return param => d((T)param);
}
Run Code Online (Sandbox Code Playgroud)

然后,您有一个委托将参数强制转换为所需类型,然后调用该HandleMessage方法.


Gab*_*abe 7

你在使用C#4吗?如果是这样,dynamic可能会加快速度:

Action<object> hook = message => ((dynamic)handler).HandleMessage((dynamic)message);
Run Code Online (Sandbox Code Playgroud)


Jon*_*son 6

你可以用Delegate::CreateDelegate.这明显快于Invoke().

var handler = Activator.CreateInstance(typeof(Handler));
var method = type.GetMethod("HandleMessage", BindingFlags.Instance | BindingFlags.Public);
var hook = (Action<object>)Delegate.CreateDelegate(typeof(Action<object>), handler, method);

// Then when you want to invoke it: 
hook(new MyMessageType()); 
Run Code Online (Sandbox Code Playgroud)

随意对它进行基准测试,但我之前已经将它变为平台,而且速度明显更快.

编辑:我现在看到你的问题,你不能按照我建议的方式去做.

您可以使用表达式编译为您执行调用的委托,这将非常快:

var type = typeof(Handler);
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("HandleMessage", BindingFlags.Instance | BindingFlags.Public);

var originalType = type;
// Loop until we hit the type we want.
while (!(type.IsGenericType) || type.GetGenericTypeDefinition() != typeof(EncasulatedMessageHandler<>))
{
    type = type.BaseType;
    if(type == null)
        throw new ArgumentOutOfRangeException("type");
}

var messageType = type.GetGenericArguments()[0]; // MyMessageType

// Use expression to create a method we can.
var instExpr = Expression.Parameter(typeof(object), "instance");
var paramExpr = Expression.Parameter(typeof(Message), "message");
// (Handler)instance;
var instCastExpr = Expression.Convert(instExpr, originalType);
// (MyMessageType)message
var castExpr = Expression.Convert(paramExpr, messageType); 
// ((Handler)inst).HandleMessage((MyMessageType)message)
var invokeExpr = Expression.Call(instCastExpr, method, castExpr); 
// if(message is MyMessageType) ((Handler)inst).HandleMessage((MyMessageType)message);
var ifExpr = Expression.IfThen(Expression.TypeIs(paramExpr, messageType), invokeExpr);

// (inst, message) = { if(message is MyMessageType) ((Handler)inst).HandleMessage((MyMessageType)message); }
var lambda = Expression.Lambda<Action<object, Message>>(ifExpr, instExpr, paramExpr);
var compiled = lambda.Compile();
Action<Message> hook = x => compiled(instance, x);

hook(new MyMessageType());
Run Code Online (Sandbox Code Playgroud)

编辑:除了上面的表达式示例,以下内容也可以使用 - 这是我在这些类型的场景中所做的.

var instance = (IEncapsulatedMessageHandler)Activator.CreateInstance(typeof(Handler));
instance.HandleMessage(new MyMessageType());

public class Message { }

public class MyMessageType : Message { }

public interface IEncapsulatedMessageHandler
{
    void HandleMessage(Message message);
}

public abstract class EncasulatedMessageHandler<T> : IEncapsulatedMessageHandler where T : Message
{
    public abstract void HandleMessage(T message);

    void IEncapsulatedMessageHandler.HandleMessage(Message message)
    {
        var msg = message as T;
        if (msg != null)
            HandleMessage(msg);
    }
}

public class Handler : EncasulatedMessageHandler<MyMessageType>
{
    public override void HandleMessage(MyMessageType message)
    {
        Console.WriteLine("Yo!");
    }
}
Run Code Online (Sandbox Code Playgroud)