【问题标题】:Fast Delegate Accessors without using DynamicInvoke()?不使用 DynamicInvoke() 的快速委托访问器?
【发布时间】:2014-01-15 21:18:34
【问题描述】:

我最近正在开发一个 SQLite ORM,并且遇到了一些关于动态事物的问题(我正在针对 C# 3.5 进行编码)。其中之一是关于如何快速获得房产价值。我尝试了许多不同的方法,其中一种非常快(几乎可以作为原生和硬编码访问),但问题是您需要使用泛型,而我只想提供类型。如果我只提供类型,我会被委托(只有缓慢而绝望的 DynamicInvoke 方法可用)或使用 MethodInfo.Invoke()...

所以我想知道当我有委托时是否有任何适当的方法来获取这个 Invoke()(而不是 DynamicInvoke)。我想将 Delegate 转换为 Func,但它不起作用。

有什么技巧吗?

class Program
{
    static void Main(String[] args)
    {
        DummyClass dummyInstance= new DummyClass();
        dummyInstance.DummyMember = "I'm a Dummy Value!";

        Type typeDummyClass = typeof(DummyClass);

        PropertyInfo propertyInfo = typeDummyClass.GetProperty("DummyMember");
        MethodInfo methodInfo = propertyInfo.GetGetMethod();

        ParameterExpression parameterExpression = Expression.Parameter(typeDummyClass, "Instance");
        Expression expression = Expression.Property(parameterExpression, propertyInfo.Name);

        Type funcType = typeof(Func<,>);
        Type funcGenericType = funcType.MakeGenericType(typeDummyClass, propertyInfo.PropertyType);

        LambdaExpression lambdaExpression = Expression.Lambda(funcGenericType, expression, parameterExpression);
        Expression<Func<DummyClass, String>> expressionTyped = Expression.Lambda<Func<DummyClass, String>>(expression, parameterExpression);
        Delegate @delegate = lambdaExpression.Compile();
        Func<DummyClass, String> func = expressionTyped.Compile();

        TimeSpan timeSpanNativeAccess = new TimeSpan(0);
        TimeSpan timeSpanGetValueCachedAccess = new TimeSpan(0);
        TimeSpan timeSpanMethodInfoCachedAccess = new TimeSpan(0);
        TimeSpan timeSpanDelegateCachedAccess = new TimeSpan(0);
        TimeSpan timeSpanFuncCachedAccess = new TimeSpan(0);

        for (UInt32 i = 0; i < 100000; i++)
        {
            Stopwatch stopwatchNativeAccess = Stopwatch.StartNew();
            var dummyNativeAccess = dummyInstance.DummyMember;
            stopwatchNativeAccess.Stop();

            Stopwatch stopwatchGetValueCachedAccess = Stopwatch.StartNew();
            var dummyGetValueCachedAccess = propertyInfo.GetValue(dummyInstance, null);
            stopwatchGetValueCachedAccess.Stop();

            Stopwatch stopwatchMethodInfoCachedAccess = Stopwatch.StartNew();
            var dummyMethodInfoCachedAccess = methodInfo.Invoke(dummyInstance, null);
            stopwatchMethodInfoCachedAccess.Stop();

            Stopwatch stopwatchDelegateCachedAccess = Stopwatch.StartNew();
            var dummyDelegateCachedAccess = @delegate.DynamicInvoke(dummyInstance);
            stopwatchDelegateCachedAccess.Stop();

            Stopwatch stopwatchFuncCachedAccess = Stopwatch.StartNew();
            var dummyFuncCachedAccess = func.Invoke(dummyInstance); // func(dummyInstance);
            stopwatchFuncCachedAccess.Stop();

            timeSpanNativeAccess += stopwatchNativeAccess.Elapsed;
            timeSpanGetValueCachedAccess += stopwatchGetValueCachedAccess.Elapsed;
            timeSpanMethodInfoCachedAccess += stopwatchMethodInfoCachedAccess.Elapsed;
            timeSpanDelegateCachedAccess += stopwatchDelegateCachedAccess.Elapsed;
            timeSpanFuncCachedAccess += stopwatchFuncCachedAccess.Elapsed;
        }

        Console.WriteLine("timeSpanNativeAccess = " + timeSpanNativeAccess.TotalMilliseconds + " ms");
        Console.WriteLine("timeSpanGetValueCachedAccess = " + timeSpanGetValueCachedAccess.TotalMilliseconds + " ms");
        Console.WriteLine("timeSpanMethodInfoCachedAccess = " + timeSpanMethodInfoCachedAccess.TotalMilliseconds + " ms");
        Console.WriteLine("timeSpanDelegateCachedAccess = " + timeSpanDelegateCachedAccess.TotalMilliseconds + " ms");
        Console.WriteLine("timeSpanFuncCachedAccess = " + timeSpanFuncCachedAccess.TotalMilliseconds + " ms");

        Console.ReadKey();
    }
}

public class DummyClass
{
    public String DummyMember { get; set; }
}

【问题讨论】:

  • 不清楚你在问什么。 你需要使用泛型而我只想提供类型是什么意思?至于速度,将表达式编译为动态方法并从中创建委托是目前最快的方法,没有检测/AOP,而且您已经知道了。顺便说一句,您的计时代码将无法正常工作。单个 get 操作花费的时间太短,Stopwatch 无法测量。
  • "所以我想知道当我有一个委托时是否有任何适当的方法来获取这个 Invoke()(而不是 DynamicInvoke)。我想将委托转换为一个 Func,但它没有工作。”还不够清楚?简而言之,我的问题是如何获得一种快速的方法来获得 getter 值。我只是感到沮丧的是,使用诸如 Action 或 Func 等泛型的委托可以提供对 Invoke 方法的访问,其效率接近本机访问,而使用简单的委托只允许我调用低效的 DynamicInvoke...
  • 我部分同意您对基准测试方法的看法(特别是关于要正确测量访问者操作的持续时间)。但无论如何(为每种技术或我所做的方式使用专用秒表覆盖一个独立的循环)都会得出相同的结论。
  • 基准测试从根本上来说是错误的。抖动优化器将完全删除 dummyInstance.DummyMember 属性 getter 调用,因为它的值根本没有被使用。它不能删除委托调用,因为它不知道委托目标方法可能有什么副作用。因此,您将委托调用与 no code 进行比较。当然它更慢 :) 你的主要错误是没有使用分析器来测量 real 代码。当你这样做时,你肯定会稍微了解一下 SQLite 从磁盘读取数据需要多长时间。与 Expression 一样,Reflection.Emit 是最快的。
  • 是的,但这确实意味着其他方法之间的比较(通过 MethodInfo 和 Delegates)被破坏了......而且我不确定当你不检查时这个小优化是否真的发生了运行该短 sn-p 代码的项目的“优化代码”复选框(我承认我没有检查与...关联的 IL 代码)。

标签: c# reflection properties lambda delegates


【解决方案1】:

您好,我找到了一种使用此处提供的代码获取 Invoke() 方法的方法:Faster way to cast a Func<T, T2> to Func<T, object>?

诀窍在于 C# 3.5 不提供任何变化的东西,因此您必须在创建转换时自带转换。

随之添加一个 UnaryExpression 来带来转换,以便得到 Func(用于 getter)

private static Func<Object, Object> GenerateAccessor(MethodInfo methodInfo)
{
    Type typeObject = typeof(Object);

    ParameterExpression @object = Expression.Parameter(typeObject, "Objet");

    UnaryExpression unaryExpression = Expression.Convert(@object, methodInfo.DeclaringType);
    MethodCallExpression methodCallExpression = Expression.Call(unaryExpression, methodInfo);
    UnaryExpression unaryExpressionBis = Expression.Convert(methodCallExpression, typeObject);

    Expression<Func<Object, Object>> epxressionFuncObjectObject = Expression.Lambda<Func<Object, Object>>(unaryExpressionBis, @object);

    return epxressionFuncObjectObject.Compile();
}

【讨论】:

  • 哦,我明白了。你想要一个Func&lt;object, object&gt;,签名像Invoke。当然,这就是这样做的方法。
  • 是的,我只是想避免 DynamicInvoke() ;)
猜你喜欢
  • 2011-04-23
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 2016-04-09
  • 2018-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多