.net – 如何删除其他对象引用的缓存项

我假设一个对象代表某种东西,你只能在内存中有一个实例.我避免重复和等于对象.

>假设一个对象由City类型的“New York”唯一标识,包含在缓存中(扩展System.Runtime.Caching.ObjectCache).此对象由另一个名为MyBusinessObject的对象引用
>缓存删除“纽约”对象但是,对象MyBusinessObject仍然引用“纽约”.垃圾收集器不会从内存中删除此对象,因为仍然有引用.
>另一个对象从缓存中请求“纽约”.缓存为“纽约”加载City的新实例

现在有两个“纽约”实例,一个是MyBusinessObject引用的(无效)和缓存引用的“纽约”的新实例.

是否有设计模式可以解决这个问题?我不希望MyBusinessObject使用City的陈旧实例.

一种可能的解决方案不会消除被引用的缓存对象,但是,如何做到这一点?

以下是我正在尝试解释的UML图:

最佳答案 在这种情况下,您的缓存不应该缓存实际对象,而是缓存正在缓存的对象的包装器,该包装器还包含有关缓存中对象状态的信息.

例如,您可以拥有一个类似的简单类,表示缓存中的项目:

public class CacheItem<T>
{
    // Since the cache is the only thing
    // that should be making CacheItems,
    // make this internal to the assembly
    // that the cache is implemented in.
    // This constructor is called before
    // an add.
    internal CacheItem(T item)
    {
        // Set the property values.
        Item = item;
    }

    // Poor-man's immutability.
    public T Item { get; private set; }

    // The backing field for IsCached, it
    // is volatile so that it can serialize
    // access for single reads/writes, which is
    // what the property does.
    // Assume it is being added when constructed.
    private volatile bool _isCached = true;

    // Only able to be set by the cache.
    // The setter is set to false when the item
    // is stale (not in the cache any longer).
    public bool IsCached 
    {
        get { return _isCached; }
        set { _isCached = value; } 
    }
}

这里的想法很简单:

>当缓存即将在缓存中输入项的新实例时,它会调用构造函数(构造函数应该只对缓存可用,如果需要,可以使CacheItem成为带有私有构造函数的嵌套类,而不是internal)将IsCached属性值设置为true.然后将该项放入缓存中.
>当项目从缓存过期时,缓存会将IsCached属性设置为false(同样,此属性应仅可由缓存访问).
>责任移动到客户端以检查CacheItem< T>看它是否陈旧.

请注意,这是一个拉动操作.如果需要,可以通过向CacheItem< T>添加事件来添加推送操作支持.像这样:

public class CacheItem<T>
{
    // Since the cache is the only thing
    // that should be making CacheItems,
    // make this internal to the assembly
    // that the cache is implemented in.
    // This constructor is called before
    // an add.
    internal CacheItem(T item)
    {
        // Set the property values.
        Item = item;
    }

    // Poor-man's immutability.
    public T Item { get; private set; }

    // The lock for the event registrations.
    // Since everything else is immutable, this needs
    // to be as well.
    private readonly object _eventLock = new object();

    // The backing field for the Expired
    // event.  Since everything else is immutable
    // this needs to be as well.
    private readonly EventHandler _expiredHandlers;

    // The expires event.
    public event EventHandler Expired
    {
        add { lock (_eventLock) _expiredHandlers += value; }
        remove { lock (_eventLock) _expiredHandlers -= value; }
    }

    // The backing field for IsCached, it
    // is volatile so that it can serialize
    // access for single reads/writes, which is
    // what the property does.
    // Assume it is being added when constructed.
    private volatile bool _isCached = true;        

    // The setter is set to false by the 
    // Expire method (called by the cached)
    // when the item is stale 
    // (not in the cache any longer).
    public bool IsCached { get { return _isCached; } }

    // Called internally by the cache.
    internal void Expire()
    {
        // Set _isCached to false.
        _isCached = false;

        // Get the event handlers and fire
        // the event.  Getting the handlers
        // needs to be synchronized.
        EventHandler handlers;

        // Synchronize.
        lock (_eventLock) handlers = _expiredHandlers;

        // Fire if there are handlers.
        if (handlers != null) handlers(this, EventArgs.Empty);
    }
}

现在,您可以让您的客户订阅Expired事件,该事件将在缓存项无效时向客户端指示.当缓存从缓存中删除时,缓存会通过调用项目上的内部Expire事件来触发此操作(并且它也会将IsCached属性设置为false).

点赞