Click here to Skip to main content
15,888,816 members
Articles / Programming Languages / C#

WeakReferences as a Good Caching Mechanism

Rate me:
Please Sign up or sign in to vote.
4.67/5 (4 votes)
10 Apr 2009CPOL4 min read 46K   491   21  
This article shows that weak references are a good caching mechanism if used with a KeepAlive structure in mind for recently used objects.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Pfz.Caching
{
	/// <summary>
	/// This class is similar to the HashSet class, but allows items to
	/// be collected.
	/// </summary>
	[Serializable]
	public class WeakHashSet<T>:
		ThreadSafeDisposable,
		IEnumerable<T>,
		IEnumerable,
		ISerializable
	where
		T: class
	{
		#region Private dictionary
			private volatile WeakDictionary<int, List<T>> fDictionary = new WeakDictionary<int, List<T>>();
		#endregion
	
		#region Constructor
			/// <summary>
			/// Creates the hashset.
			/// </summary>
			public WeakHashSet()
			{
			}
		#endregion
		#region Dispose
			/// <summary>
			/// Frees all handles.
			/// </summary>
			protected override void Dispose(bool disposing)
			{
				if (disposing)
				{
					var dictionary = fDictionary;
					if (dictionary != null)
					{
						fDictionary = null;
						
						dictionary.Dispose();
					}
				}
				
				base.Dispose(disposing);
			}
		#endregion
		
		#region Methods
			#region Contains
				/// <summary>
				/// Verifies if this hashset contains an specific item.
				/// </summary>
				/// <param name="item">The item to check to be part of this hashset.</param>
				/// <returns>true if the item is in this hashset, false otherwise.</returns>
				public bool Contains(T item)
				{
					if (item == null)
						throw new ArgumentNullException("item");
					
					int hashCode = item.GetHashCode();
					lock(DisposeLock)
					{
						CheckUndisposed();

						var items = fDictionary[hashCode];
						if (items == null)
							return false;
						
						foreach(T otherItem in items)
							if (otherItem.Equals(item))
								return true;
						
						return false;
					}
				}
			#endregion

			#region Add
				/// <summary>
				/// Adds an item to this hashset.
				/// </summary>
				/// <param name="item">The item to add in the hashset.</param>
				/// <returns>
				/// Returns true if the item was added, or false if it was already
				/// in the hashset.
				/// </returns>
				public bool Add(T item)
				{
					if (item == null)
						throw new ArgumentNullException("item");
					
					int hashCode = item.GetHashCode();
					lock(DisposeLock)
					{
						CheckUndisposed();
						
						var dictionary = fDictionary;
						List<T> items = dictionary[hashCode];
						if (items == null)
						{
							items = new List<T>(1);
							dictionary.Add(hashCode, items);
							items.Add(item);
							return true;
						}
						
						foreach(T otherItem in items)
							if (item.Equals(otherItem))
								return false;
								
						items.Add(item);
						return true;
					}
				}
			#endregion
			#region Remove
				/// <summary>
				/// Removes an item from this hashset.
				/// </summary>
				/// <param name="item">The item to remove.</param>
				/// <returns>true if the item was present in the hashset, false otherwise.</returns>
				public bool Remove(T item)
				{
					if (item == null)
						throw new ArgumentNullException("item");
					
					int hashCode = item.GetHashCode();
					lock(DisposeLock)
					{
						CheckUndisposed();
						
						var items = fDictionary[hashCode];
						if (items == null)
							return false;
						
						int count = items.Count;
						for (int i=0; i<count; i++)
						{
							if (item == items[i])
							{
								items.RemoveAt(i);
								return true;
							}
						}
						
						return false;
					}
				}
			#endregion

			#region GetEnumerator
				/// <summary>
				/// Gets an enumerator with all items that exist in this hashset.
				/// Different from the common HashSet, they are not guaranteed to 
				/// be in the order they were added.
				/// </summary>
				public IEnumerator<T> GetEnumerator()
				{
					lock(DisposeLock)
					{
						CheckUndisposed();
						
						List<T> result = new List<T>(fDictionary.Count);
						
						var dictionary = fDictionary;
						foreach(var items in dictionary.Values)
							foreach(T item in items)
								result.Add(item);
						
						return result.GetEnumerator();
					}
				}
			#endregion
		#endregion
		
		#region ISerializable Members
			/// <summary>
			/// Creates the hashset from serialization info.
			/// Actually, it does not load anything, as if everything was
			/// collected.
			/// </summary>
			public WeakHashSet(SerializationInfo info, StreamingContext context):
				this()
			{
			}

			/// <summary>
			/// It is here to be inherited. Actually, this does not
			/// add anything, as if everything was collected.
			/// </summary>
			protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
			{
			}
		
			void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
			{
				lock(DisposeLock)
				{
					CheckUndisposed();
					
					GetObjectData(info, context);
				}
			}
		#endregion
		#region IEnumerable Members
			IEnumerator IEnumerable.GetEnumerator()
			{
				return GetEnumerator();
			}
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions