【问题标题】:Precompile Lambda Expression Tree conversions as constants?将 Lambda 表达式树转换预编译为常量?
【发布时间】:2010-07-18 01:25:38
【问题描述】:

获取表达式树并将其转换为其他形式是相当普遍的,例如字符串表示形式(例如this questionthis question,我怀疑 Linq2Sql 会做类似的事情)。

在许多情况下,甚至可能是大多数情况下,表达式树转换总是相同的,即如果我有一个函数

public string GenerateSomeSql(Expression<Func<TResult, TProperty>> expression)

那么任何具有相同参数的调用将始终返回相同的结果,例如:

GenerateSomeSql(x => x.Age)  //suppose this will always return "select Age from Person"
GenerateSomeSql(x => x.Ssn)  //suppose this will always return "select Ssn from Person"

因此,本质上,带有特定参数的函数调用实际上只是一个常数,除了在运行时不断地重新计算它会浪费时间。

假设,为了论证,转换足够复杂,导致明显的性能损失,有没有办法将函数调用预编译成实际的常量?

编辑 似乎没有办法在 C# 本身内完全做到这一点。您可能在 c# 中最接近的可能是接受的答案(尽管您当然要确保缓存本身不比重新生成慢)。要实际转换为真正的常量,我怀疑通过一些工作,您可以使用 mono-cecil 之类的东西在编译后修改字节码。

【问题讨论】:

    标签: c# .net linq


    【解决方案1】:

    出色的LINQ IQueryable Toolkit 项目有一个查询缓存,其功能与您所描述的类似。它包含一个ExpressionComparer 类,该类遍历两个表达式的层次结构并确定它们是否等效。此技术还用于收集对公共属性的引用以进行参数化和删除冗余连接。

    您需要做的就是想出一个表达式哈希策略,这样您就可以将处理后的表达式的结果存储在字典中,以备将来重用。

    您的方法将如下所示:

    private readonly IDictionary<Expression, string> _cache
        = new Dictionary<Expression, string>(new ExpressionEqualityComparer());
    
    public string GenerateSomeSql(Expression<Func<TResult, TProperty>> expression)
    {
        string sql;
        if (!_cache.TryGetValue(expression, out sql))
        {
            //process expression
            _cache.Add(expression, sql);
        }
        return sql;
    }
    
    class ExpressionEqualityComparer : IEqualityComparer<Expression>
    {
        public bool Equals(Expression x, Expression y)
        {
            return ExpressionComparer.AreEqual(x, y);
        }
    
        public int GetHashCode(Expression obj)
        {
            return ExpressionHasher.GetHash(obj);
        }
    }
    

    【讨论】:

      【解决方案2】:

      首先,我怀疑您关于编译表达式会导致性能下降的假设实际上不会成立。我的经验表明,在常规“好”代码导致问题之前,还有更多因素(数据库访问、网络延迟、非常糟糕的算法)会导致性能瓶颈。过早的优化是万恶之源,因此请构建您的应用程序并运行压力测试以找出实际的性能瓶颈,因为它们通常不是您所期望的。

      话虽如此,我认为预编译取决于表达式被翻译成什么。我知道使用 LINQ to SQL 您可以调用DataContext.GetCommand(Expression) 并检索DBCommand,然后您可以缓存和重用它。

      【讨论】:

        猜你喜欢
        • 2012-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多