缓存扩展方法的返回值

时间:2012-01-26 21:27:32

标签: c# extension-methods

如果我可以修改有问题的类

,这是伪代码
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 个答案:

答案 0 :(得分:3)

您可以让您的扩展方法使用缓存管理器类(自定义或内置到框架中),该类根据实例的某些唯一标识符跟踪数据:

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 :(得分:1)

这非常适合于this blog post of mine中描述的功能缓存。我们的想法是使用一个参数获取一个函数并将其转换为另一个函数来缓存原始函数的结果:

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
    }
}

这里很好的部分是为所有函数编写了一次缓存,因此无论你需要缓存什么,都可以重用相同的方法。这也避免了因使用静态缓存而导致的内存泄漏。

这也可以以线程安全的方式完成。有关演练,请参阅second post in the series

答案 2 :(得分:0)

我不太确定你想要完成什么,但是如果我遵循,它会看起来像。

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
{
}