【发布时间】: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