我假设一个对象代表某种东西,你只能在内存中有一个实例.我避免重复和等于对象.
>假设一个对象由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).