2011-02-15 89 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,他們似乎找到了解決辦法。

聲明:我知道這是一個老問題,更多關於形成 適當的關鍵,而不是更好地實現緩存。但我認爲今天有人在尋找這個,可能會覺得它很有用。

相關問題