Welcome

首页 / 软件开发 / 数据结构与算法 / 谈表达式树的缓存(2):由表达式树生成字符串

谈表达式树的缓存(2):由表达式树生成字符串2011-09-30 博客园 Jeffrey Zhao谈到使用表达式树作为key进行缓存,您脑海中最早浮现出来的解决方案是什么?老赵看来,大部分朋 友的第一反应自然就是将作为key的表达式树,使用一定规则生成一个字符串。简而言之,这个生成字符 串的规则F需要能够保证:

在同一个缓存空间内,同样的表达式树能够生成相同的字符串。

在同一个缓存空间内,不同的表达式树生成不同的字符串。

似乎有些罗嗦,朋友们明白便是。其中“在同一个缓存空间”的前提,其实只是放宽了后续要求的条 件。因为在不同的缓存空间内,即使不同的表达式树生成了同样的字符串,它们也不会冲突;同理,不同 缓存空间内的相同表达式树,也不一定非要得到相同的字符串——事实上,不同的缓存空间很可能对于字 符串有不同的要求,这一点强求不得。

例如,在上一篇文章的例子中,构建“GetUserByID_100”和“GetUserByName_jeffz”两个字符串一 般已经足够了。因为我们往往会将它们放在同一个缓存空间中(例如,UserService中的Cache容器),而 与其它的缓存空间(例如AdminService)完全隔离。而如果打破了这个限制,那么这样的字符串生成规则 就不够用了,我们就要设计新的字符串规则(如UserService_GetUserByID_100)。

对于之前的例子来说,构造简单的“GetUserByID_100”这种字符串已经足够进行缓存了,不过如果要 把一个表达式树转化为一个字符串,并不是一件容易的事情。有朋友在我上一篇的文章后面回复到“直接 用Expression<T>的ToString方法不行么?”答案是显而易见的(希望朋友们能够少猜测,多实践 ),例如:

Expression<Func<int, int>> exp1 = i => i;
Expression<Func<long, long>> exp2 = i => i;

Console.WriteLine(exp1.ToString());
Console.WriteLine(exp2.ToString());

您会发现两个明显不同的表达式树ToString后得到了同样的内容。表达式树的ToString方法是丢失信 息的。例如,如果表达式树中涉及方法调用,那么ToString也只会包含方法名,而无法表现出方法所属的 类,以及它的返回值。如果要把一个表达式树完整地生成字符串,自然要用到ExpressionVisitor。我们 这里把转化用的类称为是SimpleKeyBuilder:

public class SimpleKeyBuilder : ExpressionVisitor
{
public string Build(Expression exp)
{
this.m_builder = new StringBuilder();
this.Visit(exp);
return this.Key;
}

public string Key { get { return this.m_builder.ToString(); } }

private StringBuilder m_builder;
}