如果对象是垃圾回收,如何获得通知?

时间:2012-01-10 13:55:30

标签: c# .net caching garbage-collection observer-pattern

短版:

对于缓存类,如果对象被垃圾收集(从缓存中删除相应的条目),我需要收到通知。这样做的最佳方法是什么?从析构函数发送事件?

长版:

我正在为一个带有一个巨大的参数树对象和许多小值类型参数的函数编写一个cacher / memoizer,例如,

double myFunc(HugeParTree parTree, int dynPar1, double dynPar2)

我想以下列方式缓存这些功能:

  • 元组的缓存结果(parTree.GUID,dynPar1,dynPar2,...)
  • 每当parTree发生更改(很少发生)时,所有相应的缓存条目都将被删除(通过Observer模式)。 (parTree.Equals()太贵了;它比较了100多种价值类型)。

Code现在看起来像这样(对于一个值参数):

public class CachedFunction1ObsPar1Par<TRet, TObsPar1, TPar1>
        where TObsPar1 : IObservable, IProvideGUID
    {
        public delegate TRet ValueCalculator(TObsPar1 obsPar1, TPar1 par1);

        public CachedFunction1ObsPar1Par(ValueCalculator calc)
        {
            _calc = calc;
        }


        #region members

        private ValueCalculator _calc;

        private Dictionary<Guid, Dictionary<TPar1, TRet>> _cache = 
            new Dictionary<Guid, Dictionary<TPar1,TRet>>();

        #endregion


        public TRet value(TObsPar1 obsPar1, TPar1 par1)
        {
            TRet result;
            bool cacheHit = checkCache(obsPar1, par1, out result);

            if (cacheHit)
            {
                Debug.Assert(result.Equals(_calc(obsPar1, par1)));
                return result;
            }
            else
            {
                result = _calc(obsPar1, par1);
                _cache[obsPar1.GUID].Add(par1, result);
                return result;
            }
        }

        private bool checkCache(TObsPar1 obsPar1, TPar1 par1, out TRet result)
        {
            if (!_cache.ContainsKey(obsPar1.GUID))
            {
                _cache.Add(obsPar1.GUID, new Dictionary<TPar1, TRet>());
                obsPar1._changed += this.invalidateCache;
            }

            Dictionary<TPar1, TRet> guidCache = _cache[obsPar1.GUID];

            bool success = guidCache.TryGetValue(par1, out result);

            return success;
        }

        private void invalidateCache(object sender)
        {
            TObsPar1 obsPar = (TObsPar1)sender;

            _cache.Remove(obsPar.GUID);

            obsPar._changed -= this.invalidateCache;
        }
    }

我还没有测试过这个问题,因为我还有一个问题,即在不再使用相应的parTree后,缓存条目永远不会被删除。我喜欢同步解决方案而不会对非常旧的缓存条目重复“扫描”。

3 个答案:

答案 0 :(得分:8)

  

对于缓存类,如果对象是垃圾,我需要收到通知   收集(从我的缓存中删除相应的条目)。是什么   最好的方法吗?从析构函数发送事件?

如果您的缓存包含正常(强)引用,则永远不会收集这些项目。

如果您的缓存包含WeakReferences,则不必删除任何内容。

答案 1 :(得分:1)

您可以定义一个必须由缓存中的对象实现的接口“ICacheable”。在接口RemoveFromCache()的方法中,您可以在缓存中搜索其子对象并将其删除。

从缓存中删除项目时,请测试该界面并调用RemoveFromCache()

这类似于IDisposable。

垃圾收集不值得依靠,因为你永远不知道它何时会运行。

答案 2 :(得分:1)

Henk已经提到了你的要求中的缺陷。

但是,只是回答你的问题。 要知道对象何时被垃圾回收,您可以为该对象编写析构函数。

~YourClass();

根据MSDN:

  

在对象变为自动调用此方法   不可访问的

虽然从不建议依赖GC或析构函数。