Overview

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.

Introduction

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.

Generics

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.

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.

Cache<Key,Value>

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:

publicclass 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:

publicinterface 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.

publicvoid 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.
Remove(k);
}
// Need to get current total size and see if this will fit.
int projectedUsage = v.BytesUsed
+ this.CurrentCacheUsage;
if (projectedUsage > maxBytes)
PurgeSpace(v.BytesUsed);
// 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()

Enumerators

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.

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!

Properties

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; }

Performance

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!

Conclusion

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!

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!

Probably not without writing your code for you. Once you've overriden the Image constructors in MyImage, you use MyImage exactly like you'd use Image.

You would need to set up your cache limits and make sure that you "touch" each image when you use it. Without knowing what you want to implement, there is a low probability that any demonstration would match exactly what you want.

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. This is similar to ICacheable.

DanMayer wrote:I'm just curious, couldn't you have accomplished the same thing using WeakReferences instead of caching?

I was not really aware of WeakReferences, as I stated at the start of the article. This was really an exercise in me using generics in order to learn them. The actual work problem required C++.

Looking at WeakReferences, the thing they don't offer is an application defined limit on the usage. Simply allowing the garbage collector to make choices is limiting. For example, I have worked on projects where we have been contracted to not exceed a certain memory usage, WeakReferences do not appear to offer that level of control.