缓存已编译的lambda表达式

时间:2009-09-22 18:28:58

标签: c# performance lambda

我需要将一些信息作为lambda表达式传递给某些方法。基本上,它是一种向数据库查询添加信息的方法。一个简单的例子是:

companyData.GetAll(
   where => "SomeField = @SOMEFIELD",
   order => "Id",
   SOMEFIELD => new Parameter {DbType = DbType.String, Value = "some value"}
)

它工作得很好,除了我需要调用LambdaExpression.Compile来获取参数对象这一事实,这会对性能产生很大的影响。

为了获得更快的结果,我参加了这个天真的缓存测试:

class ExpressionCache<T,U>
{

    private static ExpressionCache<T, U> instance;
    public static ExpressionCache<T, U> Instance
    {
        get
        {
            if (instance == null) {
                instance = new ExpressionCache<T, U>();
            }
            return instance;
        }
    }

    private ExpressionCache() { }

    private Dictionary<string, Func<T, U>> cache = new Dictionary<string, Func<T, U>>();

    public Func<T, U> Get(Expression<Func<T, U>> expression)
    {
        string key = expression.Body.ToString();
        Func<T,U> func;
        if (cache.TryGetValue(key, out func)) {
            return func;
        }
        func = expression.Compile();
        cache.Add(key, func);
        return func;
    }
}

这个类产生了巨大的差异:从10000次迭代的大约35000毫秒到大约700次。

现在出现了一个问题:我将使用表达式body作为字典的键来遇到什么样的问题?

2 个答案:

答案 0 :(得分:10)

Lambda表达式可以创建闭包。当发生这种情况时,表达式主体不包含进入结果代码的所有内容。您可能会遗漏包含的潜在重要局部变量。

答案 1 :(得分:10)

我不清楚为什么你需要表达树,如果你正在将它们编译成代表。为什么不直接使用委托开始并让编译器将lambda表达式转换为委托而不是表达式树?

至于使用字符串的主体 - 你可能会遇到奇怪的情况,你使用不同的类型,但使用相同的属性名称。鉴于您已经将类型用作泛型类型参数,但这可能不是问题...我认为它会起作用,尽管我想发誓

哦,顺便说一句,你的单例缓存不是线程安全的。我建议您在静态初始化程序中初始化instance变量。这样可以使代码更简单,也更安全......

private static readonly ExpressionCache<T, U> instance
     = new ExpressionCache<T, U>();
public static ExpressionCache<T, U> Instance { get { return instance; } }