Global Caching

snarfblam

Ultimate Contributor
Joined
Jun 10, 2003
Messages
2,097
Location
USA
I posted this class definition on another thread and decided that it would be a good idea to post it in the Code Library as well. It is a generic, global object caching class, working in a dictionary-like manner (and, in fact, backed by a Dictionary<string, TValue> object).

Because of the way that .Net handles fields of static classes you are provided a complete and separate cache for each type. (Or, to be more accurate, for each type parameter. For example, you can store a Bitmap in Cache<Image> and it will not be found in Cache<Bitmap>. Likewise, a Bitmap stored in Cache<Bitmap> will not be found in Cache<Image>.) The Cache class provides indexers and enumerators, and is implemented in a manner that makes the use of keys to identify objects completely optional.

C#:
static class Cache<T>
{
    // Backing collection
    static Dictionary<string, T> values;
    
    static Cache() {
        // Create a dicationary with case-insensitive strings for keys.
        values = new Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase);
    }
 
    public static T GetItem(string key){
        return values[key];
    }
 
#region Add/Remove methods
    /// <summary>
    /// Adds an object with a specified key to the cache.
    /// </summary>
    /// <param name="key">The key under which the object is stored.</param>
    /// <param name="value">The object to store.</param>
    public static void Add(string key, T value){
        values.Add(key, value);
    }
    /// <summary>
    /// Adds an object to the cache, generating a key automatically.
    /// </summary>
    /// <param name="value">The value to store.</param>
    public static void Add(T value){
        values.Add(
            //Create a key based on the object's hash code, ideally
            //ensuring that unique objects obtain unique keys.
            value.GetHashCode().ToString(),
            value);
    }
 
    /// <summary>
    /// Removes an object from the cache by specifying the key.
    /// </summary>
    /// <param name="key">The key of the object to remove from the cache.</param>
    /// <returns>A value indicating success or failure of the operation.</returns>
    public static bool Remove(string key) {
        return values.Remove(key);
    }
    /// <summary>
    /// Removes an object from the cache by specifying the key.
    /// </summary>
    /// <param name="value">The object to remove from the cache.</param>
    /// <returns>A value indicating success or failure of the operation.</returns>
    public static bool Remove(T value) {
        string key = null;
        foreach(KeyValuePair<string, T> item in values){
            if(item.Value.Equals(value)) {
                key = item.Key;
                break;
            }
        }
 
        if(key == null) return false;
 
        return values.Remove(key);
    }
 
    /// <summary>
    /// Removes any items from the cache where the item meets the condition specified.
    /// </summary>
    /// <param name="condition">A delegate which processes an item and returns a boolean value
    /// indicative of whether or not the value meets a condition.</param>
    public static void Remove(Predicate<T> condition) {
        string[] keys = new string[values.Count];
        values.Keys.CopyTo(keys, 0);
 
        foreach(string key in keys) {
            if(condition(values[key])) values.Remove(key);
        }
    }
    /// <summary>
    /// Removes any items from the cache where the key meets the condition specified.
    /// </summary>
    /// <param name="condition">A delegate which processes a key and returns a boolean value
    /// indicative of whether or not the value meets a condition.</param>
    public static void Remove(Predicate<string> condition) {
        string[] keys = new string[values.Count];
        values.Keys.CopyTo(keys, 0);
 
        foreach(string key in keys) {
            if(condition(key)) values.Remove(key);
        }
    }
 
    /// <summary>
    /// Removes all items from the cache.
    /// </summary>
    public static void Clear() {
        values.Clear();
    }
#endregion
 
#region Enumerators
    /// <summary>
    /// Gets an enumerable collection of all the objects stored in the cache.
    /// </summary>
    public static IEnumerable<T> Values {
        get {
            return values.Values;
        }
    }
    /// <summary>
    /// Gets an enumerable collection of all the keys that identify objects in the cache.
    /// </summary>
    public static IEnumerable<string> Keys {
        get {
            return values.Keys;
        }
    }
#endregion
 
}

The Cache<T> class generally performs the same function that a statically declared Dictionary<TKey, TValue> object would, but it can be more convinient to write code with, and, again, the use of keys is optional.

Unfortunately, because of a technical issue (overloading), Cache<string> will be difficult if not impossible to use.

Here is an example of using the Cache<T> class.
C#:
public void Example(){
    // It could be handy to cache bitmaps that will be used application-wide.
    Cache<Image>.Add("OpenIcon", Image.FromFile(IconPath.OpenIcon));
    Cache<Image>.Add("CloseIcon", Image.FromFile(IconPath.CloseIcon));
 
    // Be careful--generics do not respect inheritance.
    // (They can't due to the way that .Net handles static members of generic classes.)

    // Throws exception because object is not cached in Cache<Bitmap>.
    Image OpenImage = Cache<Bitmap>["OpenIcon"]; 
    // Works
    OpenImage = Cache<Image>["OpenIcon"];

    // Cache is enumerable.
    // Let's display the contents of Cache<Image> using an image displaying form.
    foreach(string key in Cache<Image>.Keys) {
        // Create the form with the key for the caption and the associated image within the form.
        frmImageDisplay = new ImageDisplayForm(key, Cache<Image>[key]);
        // And show.
        frmImageDisplay.ShowDialog();
    }
}
 
Back
Top