【发布时间】:2016-09-09 10:52:16
【问题描述】:
我需要调用一个泛型类的实例方法。签名如下所示:
public class HandlerFactory
{
public static IHandler<T> Create<T>();
}
public interface IHandler<T>
{
T Read(Stream s);
void Write(Stream s, T v);
}
我设法通过使用表达式和 DynamicInvoke 使其工作。遗憾的是 DynamicInvoke 的性能并不是那么好。我无法将委托转换为 Action<MemoryStream, T>,因为我在编译时不知道类型。
public class Test
{
public static void Write(MemoryStream s, object value)
{
var del = GetWriteDelegateForType(value.GetType());
// TODO: How to make this faster?
del.DynamicInvoke(s, value);
}
private static object GetHandlerForType(Type type)
{
var expr = Expression.Call(typeof(HandlerFactory), "Create", new[] { type });
var createInstanceLambda = Expression.Lambda<Func<object>>(expr).Compile();
return createInstanceLambda();
}
private static Delegate GetWriteDelegateForType(Type type)
{
var handlerObj = GetHandlerForType(type);
var methodInfo = handlerObj.GetType().GetMethod("Write", new[] { typeof(MemoryStream), type });
var arg1 = Expression.Parameter(typeof(MemoryStream), "s");
var arg2 = Expression.Parameter(type, "v");
var handlerObjConstant = Expression.Constant(handlerObj);
var methodCall = Expression.Call(handlerObjConstant, methodInfo, arg1, arg2);
var lambda = Expression.Lambda(methodCall, arg1, arg2);
return lambda.Compile();
}
}
请注意,我没有对 lambda 生成进行基准测试,只是对 DynamicInvoke 的调用。
有什么办法可以用更快的东西代替 DynamicInvoke?
更新:我评估了包含代码示例的 3 个答案,并选择 Lasse V. Karlsen 答案,因为它很简单。 (注意 Grax 的代码:尽管缓存了 MakeGenericMethod 调用,但它似乎比将 Invoke 包装在委托中要慢得多)
Method | Median | StdDev |
------------------- |-------------- |----------- |
MyLambda | 1,133.2459 ns | 25.1972 ns |
ExplicitCall | 0.6450 ns | 0.0256 ns |
Test2DelegateLasse | 10.6032 ns | 0.2141 ns |
LambdaGroo | 10.7274 ns | 0.1099 ns |
InvokeGrax | 349.9428 ns | 14.6841 ns |
【问题讨论】:
-
你能创建一个人们可以修改的minimal reproducible example吗?对一组特定的现有代码进行计时,这样我就可以在我的计算机上运行它以获得基线,然后对其进行修改以查看是否可以找到更快的方法?
-
拥有一个插件工厂的通用接口并没有什么意义,恕我直言。
-
这是一个第三方库,所以我无法确定它是否有意义。这是一个演示性能的完整工作示例:pastebin.com/K3q4dgMk。在我的 PC 上,将直接方法调用与 DynamicInvoke 进行比较需要 820 毫秒到 2 毫秒。
-
我看不出将 Invoke 包装在委托中对您有何帮助。委托是特定于类型的。如果您已经知道类型,只需直接调用显式处理程序即可。否则你仍然需要一种从对象类型到显式类型调用的机制。