采用表达式树并将其转换为某种其他形式,例如字符串表示(例如 this question 和 this 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 的东西在编译后修改字节码。
最佳答案
优秀LINQ IQueryable Toolkit project 有一个查询缓存,它的功能与您所描述的类似。它包含一个 ExpressionComparer
类,该类遍历两个表达式的层次结构并确定它们是否相等。此技术还用于收集对公共(public)属性的引用以进行参数化和删除冗余连接。
您需要做的就是想出一个表达式哈希策略,这样您就可以将处理过的表达式的结果存储在字典中,以备将来重用。
你的方法看起来像这样:
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);
}
}
关于c# - 将 Lambda 表达式树转换预编译为常量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3058181/