In a previous article, I examined the details of using a simple .NET cache. That was merely an appetizer. Now, it's time to expand upon the basic cache we developed in that article and add the ability to expire items from the cache.
Listing A contains the code we developed last time.
New and improved
To build a timed cache, we must be able to determine whether an object is expired. For the purposes of this article, I'll assume the user can determine the exact expiration date. This may be enhanced in the future to allow objects placed in the cache to determine their own expiration date.
The insertCachedObject method will be modified to support the expiration of the object from the cache. This allows users to provide an expiration date when objects are inserted into the cache. The date parameter is named dtExpiration. The method stores the expiration date passed via the parameter. There are many ways to do this. For instance, we could create a wrapper object containing a date and object property. If we followed this approach, the insertCachedObject method would create an instance of the wrapper and populate the two properties. We'll opt for a less complex design calling for the declaration and creation of a second HashTable, designed to run parallel to the first. This second HashTable is called htExpiration.
The changes to the insertCachedObject method appear in Listing B.
Now, we need a way to discover expired items. The lazy and uninspired approach would include checking each cached object when it is requested through the getCachedObject method. This is a poor approach because it leaves expired objects in the cache until they are requested. This could be an indefinite, and possibly, infinite period of time. While these expired objects reside in the cache, they consume valuable memory, adversely affecting application performance.
In lieu of this lazy approach, we'll create a background thread that iterates through the htExpiration HashTable.
We'll create a class called TheReaperThread. It has one method establishing an infinite loop. It is declared as an inner class, since it is used only within the context of the CustomCache object. The code for the entire class appears in Listing C.
Make certain that you pay close attention to the Shared Constructor in Listing C, which is responsible for creating a new thread and starting its execution. We will now create the test program to ensure that the cache is working operationally. The test program in Listing D is created as a Console Application, although it could be any project created using the .NET Framework.
The timed cache is now complete. Although functional, it is still simple—it provides no facility to notify users of the cache when items expire, nor does it allow objects to determine their own expiration algorithms. We'll take this simple cache and continue to expand on it in future discussions of delegates, memory management, and more.