Кэширование возвращаемого значения метода расширения - PullRequest
2 голосов
/ 27 января 2012

Вот псевдокод, на что это было бы похоже, если бы я мог изменить рассматриваемый класс

public class Foo
{
    private TheDataType _Data;
    public TheDataType GetData()
    {
        if (_Data != null)
            return _Data;

        // fetch/generate data

        return _Data;
    }
}

Как мне превратить это в метод расширения? Определение / сфера действия _Data меня беспокоит ...

=============================================== =

Это, вероятно, ближе всего к тому, что я хочу, но чувствую себя излишним для чего-то такого простого

public static class FooExtensions
{
    private static ConcurrentDictionary<Foo,TheDataType> DataCache = new ConcurrentDictionary<Foo,TheDataType>();
    public static TheDataType GetData(this Foo foo)
    {
        TheDataType data;
        if (DataCache.TryGetValue(foo, out data))
            return data

        // fetch/generate data

        DataCache.Add(foo, data);
        return data;
    }
}

Ответы [ 3 ]

3 голосов
/ 27 января 2012

Ваш метод расширения может использовать класс менеджера кэша (пользовательский или встроенный в платформу), который отслеживает данные на основе некоторого уникального идентификатора для экземпляра:

public static DataType GetData( this Foo obj )
{
    DataType retVal;

    // this sample doesn't show any locking, i.e. it is not thread safe

    // if cache manager contains data return from there
    if( CacheManager.HasData( obj.UniqueId ) )
    {
         retVal = CacheManager.GetData( obj.UniqueId );
    }    
    else
    {
         // otherwise invoke a method on obj and add to cache
         retVal = obj.GetData();
         CacheManager.Add( obj.UniqueId, retVal );
    }

    return retVal;
}

Однако это поражаетя как потенциальное неправильное использование метода расширения, хотя он синтаксически чистый.Это будет зависеть от контекста, в котором он используется, и от того, насколько очевидны побочные эффекты.Например, другим разработчикам было бы очень трудно устранить неполадки, если они не знали, что GetData () использует кэш, срок действия кэш-памяти не был ясен и т. Д.

1 голос
/ 27 января 2012

Это хорошо подходит для кэширования функций, описанных в этом моем блоге .Идея состоит в том, чтобы взять функцию с одним аргументом и превратить ее в другую функцию, которая кэширует результат исходного:

public static Func<TKey, TValue> Cached<TKey, TValue>(this Func<TKey, TValue> valueFunction)
{
    var cache = new Dictionary<TKey, TValue>();

    return key =>
    {
        TValue value;

        if(!cache.TryGetValue(key, out value))
        {
            value = valueFunction(key);

            cache[key] = value;
        }

        return value;
    };
}

Словарь кеша внедряется в возвращаемое закрытие, поэтому он будет иметь тот жевремя жизни функции, которую мы возвращаем.

Вы бы использовали ее, заменив исходный вызов, чтобы получить данные новой функцией:

public class UsesTheDataType
{
    private readonly Func<Foo, TheDataType> _generateData;

    public UsesTheDataType()
    {
        _generateData = GenerateData;

        _generateData = _generateData.Cached();
    }

    public void UseTheDataType(Foo foo)
    {
        var theDataType = _generateData(foo);

        // theDataType is either a new value or cached value
    }

    private TheDataType GenerateData(Foo foo)
    {
        // Only called the first time for each foo
    }
}

Приятная часть в том, что кеширование написаноодин раз для всех функций , так что вы можете использовать один и тот же подход независимо от того, что вам нужно для кеширования.Это также позволяет избежать возможных утечек памяти в результате использования статического кэша.

Это также может быть выполнено потокобезопасным способом.См. второй пост в серии для ознакомления.

0 голосов
/ 27 января 2012

Я не совсем уверен, чего вы пытаетесь достичь, но если я последую, это будет выглядеть примерно так.

public static class FlargExtensions
{
    private static Flarg flarg;

    public static Flarg GetData(this Flarg flarg)
    {
        if (FlargExtensions.flarg != null)
            return Class1.flarg;

        FlargExtensions.flarg = new Flarg();
        return FlargExtensions.flarg;
    }
}

public class Flarg
{
}
...