取一个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之类的方法来修改字节码。
优秀的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)
| 归档时间: |
|
| 查看次数: |
898 次 |
| 最近记录: |