缓存已编译的 lambda 表达式

发布于 2024-08-05 00:58:20 字数 1386 浏览 1 评论 0原文

我需要将一些信息作为 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 毫秒。

现在的问题是:使用表达式主体我会遇到什么样的问题作为字典的键?

I need to get some info passed as a lambda expression to some methods. Basically, it's a way to add information to a database query. A simple example would be:

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

It's working pretty well, except for the fact that I need to call LambdaExpression.Compile to get the parameter object, which have a big perfomance impact.

To get faster results, I've come with this naive caching test:

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;
    }
}

This class made a huge difference: from about 35000 milliseconds on 10000 iterations to about 700.

Now comes the question: what kind of problem I will run into using the expression body as the key for the dictionary?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眸中客 2024-08-12 00:58:20

我不清楚如果您将表达式树编译为委托,为什么还需要表达式树。为什么不直接使用委托来让编译器将 lambda 表达式转换为委托而不是表达式树呢?

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

哦,顺便说一下,你的单例缓存不是线程安全的。我建议您在静态初始化程序中初始化实例变量。这会导致代码更简单并且更安全......

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

It's not clear to me why you need expression trees at all if you're compiling them down to delegates. Why not just use delegates to start with and get the compiler to convert the lambda expression into a delegate instead of into an expression tree?

As for using the body of the string - you may get into odd cases where you use different types but the same property names. Given that you're already using the types as generic type parameters, however, that's probably not an issue... I should think it'll work, although I would like to swear to it.

Oh, and your singleton cache isn't thread-safe by the way. I would suggest that you initialize the instance variable in a static initializer. This leads to simpler code as well as it being safer...

private static readonly ExpressionCache<T, U> instance
     = new ExpressionCache<T, U>();
public static ExpressionCache<T, U> Instance { get { return instance; } }
沙与沫 2024-08-12 00:58:20

Lambda 表达式可以创建闭包。当发生这种情况时,表达式主体并不包含生成代码中的所有内容。您可能会丢失包含的潜在重要局部变量。

Lambda expressions can create closures. When that happens, the expression body doesn't encompass everything that goes into the resulting code. You could be missing potentially important local variables that are included.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文