我有一个用于检查方法参数的类,您可以在表单中调用:
public void SomeMethod(string anArg)
{
Ensure.ArgumentNotNull(() => anArg);
}
如果参数为null,则抛出具有该属性名称的ArgumentNullException
。这样做是这样的:
public static void ArgumentNotNull<T>(Expression<Func<T>> expression) where T : class
{
var value = expression.Compile()();
if (value == null)
{
throw new ArgumentNullException(expression.GetMemberName());
}
}
GetMemberName
是我编写的扩展方法。
我遇到的问题是对Compile的调用非常慢,所以我想缓存结果,但我似乎无法想出一个足够独特的缓存键防止缓存冲突,但不是那么独特,以致缓存变得无效。
到目前为止我的最大努力是:
internal static class ExpressionCache<T>
{
private static readonly Dictionary<string, Func<T>> Cache = new Dictionary<string, Func<T>>();
public static Func<T> CachedCompile(Expression<Func<T>> targetSelector)
{
Func<T> cachedFunc;
var cacheKey = targetSelector + targetSelector.Body.ToString();
if (!Cache.TryGetValue(cacheKey, out cachedFunc))
{
cachedFunc = targetSelector.Compile();
Cache[cacheKey] = cachedFunc;
}
return cachedFunc;
}
}
但这仍会导致缓存密钥冲突。什么是更好的方法?
答案 0 :(得分:2)
exrpessions来自哪里,是否创造了新的?如果它们被重用,你可以只使用表达式本身作为键。:
internal static class ExpressionCache<T>
{
private static readonly Dictionary<Expression<Func<T>, Func<T>> Cache = new Dictionary<Expression<Func<T>, Func<T>>();
public static Func<T> CachedCompile(Expression<Func<T>> targetSelector)
{
Func<T> cachedFunc;
if (!Cache.TryGetValue(targetSelector, out cachedFunc))
{
cachedFunc = targetSelector.Compile();
Cache[targetSelector] = cachedFunc;
}
return cachedFunc;
}
}
否则你可以窥探DLR http://dlr.codeplex.com/的源代码,我相信他们很好地解决了这类问题。
答案 1 :(得分:1)
如果您更关注竞争条件和可读性而不是性能(我不确定它是否会变得最糟糕),而不是使用Dictionary<T,V>
,您可以考虑使用ConcurrentDictionary<T,V>
它已经有一个GetOrAdd
方法,可以让你编写更少的代码,而且随着.NET 4.0的推出,它可以确保工作并有良好的文档记录。
var dict = new ConcurrentDictionary<Expression<Func<T>, Func<T>>();
...
var cacheKey = targetSelector; //or whatever as long as it's unique
var cachedFunc = dict.GetOrAdd(cacheKey, key => targetSelector.Compile());
此外,它可能会更容易出现竞争条件。但是你必须知道GetOrAdd
也不是线程安全的。如果您关心这一点,请查看它们似乎找到解决方案的question on CodeReview.SE。
免责声明:我知道这是一个老问题,更多的是关于形成 一个正确的关键,而不是更好的缓存实现。 但我认为今天寻找这个的人可能会发现它很有用。
答案 2 :(得分:0)
Jeffery Zhao对此主题有some excellent posts,不幸的是它们是用中文写的。一个好消息是您可以下载ExpressionTree缓存here的完整实现代码。我个人还有另一个建议:为什么不Code Contracts?