缓存已编译的lambda表达式

Fer*_*ndo 4 c# performance lambda

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

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

它工作得很好,除了我需要调用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Joe*_*orn 10

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

  • 任何解决方案仍然可以实现缓存? (3认同)

Jon*_*eet 10

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

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

哦,顺便提一句,你的单例缓存不是线程安全的.我建议你instance在静态初始化器中初始化变量.这导致代码更简单,更安全......

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

  • 为"哦,你的单例缓存不是线程安全的+1". (2认同)