Welcome

首页 / 软件开发 / 数据结构与算法 / 谈表达式树的缓存(1):引言

谈表达式树的缓存(1):引言2011-09-30 博客园 Jeffrey Zhao表达式树(Expression Tree)是.NET 3.5中引入的一种表达方式。表达式树的运用十分广泛,可以直 观地表现出各种“数据”,甚至“逻辑”和“行为”。再者,表达式树是强类型的,因此合理地使用这个 新特性可以让代码编写变得优雅,方便。一个最简单而常见的例子便是,某些朋友目前就已经喜欢使用表 达式树来代替传统的ByXxx方法,尤其是在访问一些直接支持表达式树的数据源时(例如IEnumerable或 LINQ to SQL)。如下:

public User GetUser(Expression<Func<User, bool>> predicate) { }

而不必写成:

public User GetUserByID(int id) { }
public User GetUserByName(string name) { }

于是在调用时便可以:

var user1 = GetUser(u => u.UserID == 1);
var user2 = GetUser(u => u.Name == "jeffz");

姑且不论这种设计方式是否合适(因为即使这个做法不合理,也不能代表所有的用法),我们先达成 一个共识,那就是“表达式树很有用”——于是我们的接下去话题看上去才会比较有价值:P。那么就上面 一个问题来说,在使用了表达式树的情况下,如何在方法中进行缓存?在ByID或ByName的情况下,我们可 以轻易地构造一些字符串作为缓存的键,例如“GetUserByID_100”或“GetUserByName_jeffz”。但是现 在呢?每次在调用时就会生成一个不同的Expression对象,就算大家“表现一致”,也无法被“识别为” 同样的对象,而直接用作缓存的键,因此处理起来并不是那么直接了当的。

您可能会说,那么就在解析表达式树的时候,识别出它是ByID还是ByName,然后再拼接出之前的字符 串。当然,您如果真的是要解决上面这个例子的问题,那么的确可以用这种方法。但是老赵现在希望可以 找到一种较为通用的,能够根据表达式树进行缓存的解决方案——事实上老赵本来就是在设计一个通用的 功能时才引发了这个需求,而这个功能也打算在详细谈完缓存问题后与大家共享。

这个缓存问题看上去简单,但是实际上在性能和功能进行权衡之后会有多种策略可以选择。老赵会在 这里谈论5种缓存策略,它们各有千秋,有的方式资源很省,性能很好;而有的方式从性能上比较落后, 资源占用也相对较高,但是在某些场景下它似乎还是唯一的解决方案。因此,至少我觉得讨论一下这个问 题也是非常有意思的事情,而且从一定程度上说,这些思考能够在一定程度上体现出算法设计与数据结构 的美妙之处(尽管相对来说它们其实非常简单)。

在这一系列文章中,老赵希望可以重现自己在思考这个问题的时候所形成的完整思考路径。相比最终 解决方案,这可能才是更有价值的东西。文章有时也会将朋友们“引入歧途”,其目的也是为了让弟兄们 一起经历一下老赵走过的弯路。到了最后,您可以会说“这死胖子真笨,怎么早没想到”(呵呵,大家莫 怪)。此外,这5种缓存策略也并非是思考的全部,事实上老赵相信还会有更好的解决方案(至少理论上 是这样的),而由于种种原因并没有在这里实现出来。因此,老赵也希望大家在看了文章之后可以一起思 考,并谈出您的看法。:)