2011-02-15 87 views
7

我有我使用的检查方法的参数,这在窗体调用类:缓存编译<Func<T>>

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是我写的扩展方法。

我遇到的问题是,编译的调用非常慢,所以我想缓存结果,但我似乎无法拿出一个独特的缓存键足以防止缓存冲突,但不是如此唯一以至于缓存变得无效。

我最大的努力迄今:

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

我会使用PostSharp或IL:http://abdullin.com/journal/2008/12/19/how-to-get-parameter-name-and-argument-value-from-c-lambda- v.html – 2011-02-15 10:22:16

+0

@Ruben,你是对的,我在发布之前在浏览器中进行了一些攻击。我会纠正它。 – ilivewithian 2011-02-15 10:23:12

回答

1

从哪里来的,他们创造了新的?如果重复使用它们,你可以只使用表达式本身作为重点:

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

而不是使用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,他们似乎找到了解决办法。

声明:我知道这是一个老问题,更多关于形成 适当的关键,而不是更好地实现缓存。但我认为今天有人在寻找这个,可能会觉得它很有用。

相关问题