使用表达式的泛型类的 C# 加速方法调用

标签 c# .net generics lambda

我需要调用泛型类的实例方法。签名看起来像这样:

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 的性能不是很好。我无法将委托(delegate)转换为 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 包装在委托(delegate)中慢得多)

             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 |

最佳答案

这样做的方法是通过一个适当的泛型方法,结束从 objectT 的转换,并跳过整个动态调用。

根据您在 pastebin 中的代码,这里是您的测试类的新版本:

public class Test2
{
    private static readonly Action<MemoryStream, object> del;

    static Test2()
    {
        var genericCreateMethod = typeof(Test2).GetMethod("CreateWriteDelegate", BindingFlags.Static | BindingFlags.NonPublic);
        var specificCreateMethod = genericCreateMethod.MakeGenericMethod(typeof(Model));
        del = (Action<MemoryStream, object>)specificCreateMethod.Invoke(null, null);
    }

    public static void Write(MemoryStream s, object value)
    {
        del(s, value);
    }

    private static Action<MemoryStream, object> CreateWriteDelegate<T>()
    {
        var handler = HandlerFactory.Create<T>();
        return delegate (MemoryStream s, object value)
        {
            handler.Write(s, (T)value);
        };
    }
}

在我的机器上,你的代码与上面一样执行:

Your test: 1285ms
My test: 20ms
Explicit: 4ms

关于使用表达式的泛型类的 C# 加速方法调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39409926/

相关文章:

c# - HTML 敏捷包

java - 类型化接口(interface)的奇怪 Java 编译错误

C# Webforms 在代码执行期间显示加载指示器

c# - 如何将流上传到Azure媒体服务

c# - 如何检查对象的所有属性是否为 null 或为空?

c# - 替换字符串 C# 中的换行符

.net - WCF 消息格式

c# - 32 位隐式转换无法通过通用重载解析

java - 变量(函数类型)参数计数和变量返回类型

c# - Python 与 C# Twitter API 库