将Lambda表达式树转换预编译为常量?

Nat*_*han 5 .net c# linq

取一个Expression树并将其转换为其他形式(例如字符串表示形式)是很常见的(例如,这个问题这个问题,我怀疑Linq2Sql做类似的事情)。

在很多情况下,即使在大多数情况下,表达式树的转换也总是相同的,即如果我有一个函数

public string GenerateSomeSql(Expression<Func<TResult, TProperty>> expression)
Run Code Online (Sandbox Code Playgroud)

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

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"
Run Code Online (Sandbox Code Playgroud)

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

出于争论的原因,假定转换足够复杂以至于引起明显的性能下降,是否有任何方法可以将函数调用预编译为实际常量?

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

Nat*_*lch 3

优秀的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);
    }
}
Run Code Online (Sandbox Code Playgroud)