技术开发 频道

谈表达式树的缓存:表达式树生字符串

    【IT168技术文档】完整的代码不止如此,虽然我们不需要覆盖(override)所有ExpressionVisitor的方法,但是一些必要的元素还是不可或缺的。不过,关键之处并不在这里。假设我们的VisitXxx方法已经能够完整地描述各数据,但是那些Accept方法足够详细吗?答案是否定的,至少之前的代码便有几点问题:

    在描述一个Type时,FullName提供的信息完整吗?是否需要AssemblyQualifiedName?(这点请朋友们自行思考,或者一起讨论一下)。

    在描述一个MemberInfo时,难道只记录它的DeclaringType和Name就够了吗?

    在描述一个Object时,会使用ToString方法来进行记录。

    显而易见,第三个问题是无法满足要求的。因此,如果您需要在正式场合使用这个方法,就必须根据您自己的需求来修改一下这方面的问题——例如,使用Serialize?亦或是,约定在此出现的每个相同类型的对象,它们的ToString方法都进行了合适地重载。

    如果您的“嗅觉”比较灵敏,应该已经发现这个解决方案的缺点了:那就是字符串会特别庞大。这点并非无法改进,例如您可以把一些重复的,占用数据量大的信息替换成数据量小的信息——其实就是传统的进行数据压缩的算法。不过这方面编程相对较为复杂,且属于优化阶段而不能说明解决方案的真正思路,因此这就留给朋友们作为练习吧。

    如果您感兴趣的话,还可以看一下http://code.msdn.microsoft.com/exprserialization,它提供了表达式树的完整的序列化功能,它可以把一个表达式树对象与XML进行双向转化。不过其字符串体积也无可避免的庞大,谁让表达式树天生就那么复杂呢?

    当然,如之前所说,Key的生成规则与缓存的划分密切相关。换句话说,如果您能在项目里对缓存空间进行适当的划分,那么在这样的前提下您也可以使用“不那么详细”的生成规则,这有可能会进一步压缩字符串的体积。

    实现了SimpleKeyBuilder,那么SimpleKeyCache的编写自然易如反掌,不加赘述:

public class SimpleKeyCache<T> : IExpressionCache<T> where T : class
{
private ReaderWriterLockSlim m_rwLock = new ReaderWriterLockSlim();
private Dictionary<string, T> m_storage = new Dictionary<string, T>();

public T Get(Expression key, Func<Expression, T> creator)
{
T value;
string cacheKey = new SimpleKeyBuilder().Build(key);

this.m_rwLock.EnterReadLock();
try
{
if (this.m_storage.TryGetValue(cacheKey, out value))
{
return value;
}
}
finally
{
this.m_rwLock.ExitReadLock();
}

this.m_rwLock.EnterWriteLock();
try
{
if (this.m_storage.TryGetValue(cacheKey, out value))
{
return value;
}

value = creator(key);
this.m_storage.Add(cacheKey, value);
return value;
}
finally
{
this.m_rwLock.ExitWriteLock();
}
}
}
 

    那么这个解决方案的时间复杂度是多少呢?假设表达式树有m个节点,缓存里有n个对象。那么从理论上说,构造一个Key的时间复杂度是O(m),而通过Key从字典里进行查询的时间复杂度是O(1),因此该解决方案的时间复杂度是O(m)。

    不过这是个理论值,其实际的结果呢?大家不妨思考一下,老赵在介绍完全部5种解决方案之后会单独开篇讨论一下这方面的问题。

原文地址:http://www.cnblogs.com/jeffreyzhao/archive/2009/03/17/expression-cache-2-simple-key-cache.html

0
相关文章