Click here to Skip to main content
13,298,965 members (38,413 online)
Click here to Skip to main content
Add your own
alternative version


62 bookmarked
Posted 10 Jul 2004

Using C# Generics to implement a Cache collection

, 10 Jul 2004
Rate this:
Please Sign up or sign in to vote.
An article on how I used Generics to implement a general purpose caching collection with user defined size limits.

Sample Image - GenericCache.jpg


This article really started out of a desire for me to learn the new generics functionality added to the newer versions of .NET and the version 2.0 of the Framework. Whilst at this moment I don't feel that a full fledged tutorial on generics is appropriate, this article shows my use of generics in solving a problem I recently encountered.


In my current job, I recently had the misfortune to discover a memory issue with our main development. We use hundreds of bitmaps at any one time. Long ago, someone thought that in order to improve performance we would cache the bitmaps in memory. Nice idea, except my current project seems to have pushed the technology further than anyone else had, and now we're out of memory because of the hundreds of Megabytes of images we have cached.

The Solution?

Thinking about the problem, it was obvious that we didn't need to cache every image, just the ones we were working with. Trying the system without any caching demonstrated why the caching support was originally added, performance started to get sluggish as the hard-disk is accessed so much. However, if an image hasn't been used for ages, then we can quite easily read that again from disk (as we had first time it was used) keeping the frequently and recently accessed images in the cache.

At work, a couple of hours of coding and I had implemented a very basic C++ cache using the STL and templates. This article however takes over from the point of me wondering how much alike the STL and the new C# generics are.


One of the things I really disliked about coding in C# was that whenever I used a collection (which I did lots) I had to worry about the types I was adding and retrieving, or else I had to create my own type-safe implementations of the collections. Whenever I use the STL in C++, this was never an issue. Constantly checking and casting objects was cluttering up my code.

On the most basic level, generics allow me to specify the types I want to be working with. Take the following two examples, the first in C# 1.x and the second in C# 2.x.

ArrayList items = new ArrayList();
items.Add("a string");
List<string> items = new List<string>();
items.Add("a string");
items.Add(123);  // Compile time error!!
items.Add(this);  // Compile time error!!

Using generics, I can specify the type that I want to have a list of, in this case strings. The compiler will then ensure that I do not start adding lots of incompatible types.


The implementation of the cache that I have created uses the signature of cache<Key,Value> which looks very similar to the signature of a hashtable or dictionary. What I wanted is to associate the cached data with some form of identifier (e.g., the filename of the bitmap I have cached). The actual class definition is shown below:

public class Cache
   where Key: IComparable
   where Value: ICacheable

Notice that there are some changes to the typical class definition. The new where keyword allows the use of generics to be refined. In the code above, I have specified that I want the Key type to support at least IComparable (this is necessary for me to search for the keys in the collections).

I have also defined a need for the values to implement ICacheable. The ICacheable interface is a very simple interface I created:

 public interface ICacheable
  int BytesUsed { get; }

All it really needs to do is supply the property BytesUsed in order to let the cache know how big its represented data is. This can be seen being accessed in the Add method.

public void Add(Key k, Value v)
  // Check if we're using this yet
  if (ContainsKey(k))
    // Simple replacement by removing and adding again, this
    // will ensure we do the size calculation in only one place.

  // Need to get current total size and see if this will fit.
  int projectedUsage = v.BytesUsed
                     + this.CurrentCacheUsage;

  if (projectedUsage > maxBytes)

  // Store this value now..
  StoreItem(k, v);

As is typical with any container class, there are a number of other methods for getting or changing the members of the collection. Implemented methods are:

  • void Remove(Key k)
  • void Touch(Key k)
  • Value GetValue(Key k)
  • bool ContainsKey(Key k)
  • Value this[Key k]
  • void PurgeAll()


In order to access the stored members of the cache, a number of enumerators are implemented, these allow getting the keys, the values, and both the key and value as a pair. The implementation of enumerators in C# 2.0 has been made very easy.

  • IEnumerator<Value> GetEnumerator()
  • IEnumerable<Key> GetKeys()
  • IEnumerable<Value> GetValues()
  • IEnumerable<KeyValuePair<Key, Value>> GetItems()
 public IEnumerable<Value> GetValues()
  foreach (KeyValuePair<Key, Value> i in cacheStore)
   yield return i.Value;

Note the new yield keyword. This, in effect, allows the control of the program execution to be passed back to (or yielded to) the caller. Next time the enumerator is accessed, the execution continues at the same point in the collection. In the above example, I am accessing the internal store, cacheStore, and enumerating it with the foreach command, however, I am exposing the value of each to the caller of GetValues(). Whilst this might take a little understanding, trust me, it is simpler than what was needed before!


There are only a few properties defined for the cache class, their functionality is largely obvious.

  • int MaxBytes { get; set; }
  • int Count{ get; } // Get the current number of items in cache
  • int CurrentCacheUsage{ get; }


As a condition of using the beta software from Microsoft, it is not possible to quote performance of this class in concrete terms. The compiler and its generated code is likely to get much faster in the future… also, it isn't really possible to directly compare generic code to non-generic code without having two different implementations. I have tested this code with many Megabytes of bitmaps and its caching performance was as good, no noticeable impact on performance. It must be noted that once an item is knocked out of the cache, the run-time system and its garbage collection may not dispose off the object for quite a while. What the cache implementation does do is allow you to control the lifespan of your objects!


The implementation of the cache class used here is very basic; it could be further enhanced in many ways. For my needs, the solution implemented is not too bad and allows me some control over how much memory I want my applications using in order to keep large resources active in memory.

Visual Studio 2005 and C# 2.0

At the time of writing, C# 2.0 is very new. I downloaded the beta from Microsoft only last week. If in the future, this article is still around, be careful to research any differences between what I have documented here and the final release!

If you want to download a copy of the beta, see MSDN.

I have not included a binary of this code because I expect the framework to change between each beta. The two C# source files supplied comprise of the implementation and the test application, drop them into a blank console solution to test!


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Ray Hayes
Product Manager
United Kingdom United Kingdom
No Biography provided

You may also be interested in...


Comments and Discussions

AnswerRe: Image C# object Pin
Ray Hayes8-Nov-07 2:05
memberRay Hayes8-Nov-07 2:05 
GeneralRe: Image C# object Pin
Ray Hayes8-Nov-07 2:31
memberRay Hayes8-Nov-07 2:31 
GeneralJust what I was looking for with a minor change to queue... Pin
EvilChuck1-Aug-07 8:25
memberEvilChuck1-Aug-07 8:25 
Questionthread safety? Pin
12stephan3413-Nov-06 9:46
member12stephan3413-Nov-06 9:46 
AnswerRe: thread safety? Pin
Ray Hayes15-Nov-06 5:30
memberRay Hayes15-Nov-06 5:30 
GeneralRe: thread safety? Pin
naive11-Mar-07 20:54
membernaive11-Mar-07 20:54 
GeneralHelp! Pin
Tim McCurdy1-Apr-06 6:43
memberTim McCurdy1-Apr-06 6:43 
Ok, I'm not exactly trying to use your code. I'm trying to make a Generic Collection which supports events. Basically, everything is working except for this Error: Cannot implicitly convert type 'GenericCollection<TKey,TItem>' to 'GenericCollection<TKey,ICollectionItem<TKey>>'

All I want is for all Items in the Generic Collection to implement this Interface ICollectionItem<TKey>. This is similar to ICacheable.
/// <summary>
/// Generic Collection Item Interface
/// </summary>
/// <typeparam name="TKey"></typeparam>
public interface ICollectionItem<TKey> : IDisposable
	where TKey : IComparable {
	/// <summary>
	/// Gets or sets the Key for the Item in the Collection
	/// </summary>
	TKey Key { get; set; }
	/// <summary>
	/// Gets or sets the Index of the Item in the Collection
	/// </summary>
	int Index { get; set; }
	/// <summary>
	/// Gets or sets the Collection that the Item is contained in
	/// </summary>
	GenericCollection<TKey, ICollectionItem<TKey>> Collection { get; set; }

This is how the Collection class is declared:
/// <summary>
/// Generic Collection Class
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TItem"></typeparam>
public class GenericCollection<TKey, TItem> : IDictionary<TKey, TItem>
	where TKey : IComparable
	where TItem : ICollectionItem<TKey> {

The Error mentioned above is coming from when I am trying to set the Collection reference in each ICollectionItem from within my Collection class.
Item.Collection = this; // This fails with the error mentioned above

Please, if anyone knows a better way for this to work or how to fix it, let me know! WTF | :WTF:
Generalyou forgot <key, value> Pin
SJ_Phoenix17-Jul-04 4:26
memberSJ_Phoenix17-Jul-04 4:26 
GeneralRe: you forgot &lt;key, value&gt; Pin
Ray Hayes18-Jul-04 23:13
memberRay Hayes18-Jul-04 23:13 
GeneralA small typo Pin
Nemanja Trifunovic12-Jul-04 6:32
memberNemanja Trifunovic12-Jul-04 6:32 
GeneralRe: A small typo Pin
Ray Hayes12-Jul-04 6:53
memberRay Hayes12-Jul-04 6:53 
QuestionWeakReference's? Pin
DanMayer11-Jul-04 19:09
memberDanMayer11-Jul-04 19:09 
AnswerRe: WeakReference's? Pin
Ray Hayes11-Jul-04 23:09
memberRay Hayes11-Jul-04 23:09 
GeneralRe: WeakReference's? Pin
bklooste27-Oct-04 20:13
memberbklooste27-Oct-04 20:13 
GeneralRe: WeakReference's? Pin
naive11-Mar-07 20:52
membernaive11-Mar-07 20:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.171207.1 | Last Updated 11 Jul 2004
Article Copyright 2004 by Ray Hayes
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid