Компиляция кэша из Expression <Func <T>> - PullRequest
8 голосов
/ 15 февраля 2011

У меня есть класс, который я использую для проверки аргументов метода, который вы вызываете в форме:

public void SomeMethod(string anArg)
{
    Ensure.ArgumentNotNull(() => anArg);
}

Если аргумент равен нулю, выдается 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;
    }
}

Но это все еще вызывает конфликты ключей кэша. Что может быть лучше?

Ответы [ 3 ]

2 голосов
/ 15 февраля 2011

Откуда берутся новые задания, они созданы новыми? Если они используются повторно, вы можете просто использовать само выражение в качестве ключа.

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 голос
/ 21 мая 2015

Вместо использования Dictionary<T,V>, если вы больше беспокоитесь о состоянии гонки и удобочитаемости, чем о производительности (я не уверен, что это будет хуже), вы можете рассмотреть возможность использования ConcurrentDictionary<T,V>.

У него уже есть метод GetOrAdd, который заставляет вас писать меньше кода, и, как и в случае с .NET 4.0, он гарантированно работает и хорошо документирован.чуть меньше ошибок, подверженных гоночным условиям.Но вы должны знать, что GetOrAdd также не безопасен для потоков.И если вас это волнует, взгляните на этот вопрос на CodeReview.SE , в котором они, кажется, находят решение этой проблемы.старый вопрос, который больше касается формирования правильного ключа, чем о лучшей реализации кэша.Но я думаю, что люди, которые ищут это сегодня, могут найти это полезным.

0 голосов
/ 15 февраля 2011

Джеффри Чжао имеет несколько отличных сообщений на эту тему, к сожалению, они написаны на китайском языкеХорошей новостью является то, что вы можете скачать полный код реализации для кэширования ExpressionTree здесь .И лично у меня есть еще одно предложение: почему бы не Кодовые контракты ?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...