この場合、キャッシュは実際のオブジェクトをキャッシュするのではなく、キャッシュされているオブジェクトのラッパーであり、キャッシュ内のオブジェクトの状態に関する情報も含まれている必要があります。
たとえば、キャッシュ内のアイテムを表す、このような単純なクラスを作成できます。
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に設定されます)。