4

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

  1. 假设一个对象由 类型的“New York”唯一标识City,包含在缓存中(扩展 System.Runtime.Caching.ObjectCache)。该对象被另一个名为MyBusinessObject

  2. 缓存删除了“New York”对象,但该对象MyBusinessObject仍指“New York”。垃圾收集器不会从内存中删除这个对象,因为仍然有引用。

  3. 另一个对象从缓存中请求“纽约”。City缓存加载“纽约”的新实例

现在有两个“New York”实例,一个MyBusinessObject是引用(无效),另一个是缓存引用的“New York”实例。

有没有一种设计模式可以解决这个问题?我不想MyBusinessObject使用City.

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

这是我在上面试图解释的 UML 图:

类图 UML

对象图

4

1 回答 1

2

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

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

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使用私有构造函数而不是内部构造嵌套类)它将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)。

于 2012-11-14T14:27:29.203 回答