Mixins for .NET


Mixins are missing from C#. There are lots of examples of emulating mixins and some really good ideas. The best ideas assume the use of C# 4.0 and use the ConditionalWeakTable class. However none of the ideas I could find were truly generic. This post is about a generic mixins mechanism for .NET and provides some C# code to allow you to store arbitrary additional information against any reference class instance.

Limitations of existing ideas

The mixin ideas I could find were associated with a specific type or required the addition of a ‘marker’ to a class. There’s a project on codeplex which aims to implement mixins but it requires that class instances are created via a special factory method. To be fair it supports .NET 4.0 but all these assume you control the classes to which you want to add values. What happens if you want to add a value to the instance of a framework class? The idea presented here builds on these other ideas but in more generic way.

WPF dependency methods

WPF introduced a mixin-like facility which allows a programmer to extend an existing class by adding values, events and so on. The idea presented here is very similar. The limitation of the WPF mechanism is that it assumes every class against which it is possible to store additional information is descended from DependencyObject. That’s only true in the WPF world.

Black box

The code is presented below and I’ll explain it later. However, you should be able to copy the class, drop it into a file and set about using it straight away without knowing anything about how it works. Here’s how you might set and recover values on a class instance:

myClass.setValue("somename", avalue);
var val = myClass.getValue("somename");

These examples use the ‘mixin’ get/set methods directly. The values are identified by a unique id (unique within a class instance, the same name can be used in any other instance at the same time). A better option is to create your own extension methods:

namespace MyMixins
{
	public static class MyMixinExtensions
	{
		public static void SomeString(this MyTest self, string value)
		{
			if (value == null)
				self.DeleteValue("SomeString");
			else
				self.SetValue("SomeString", value);
		}

		public static string SomeString(this MyTest self)
		{
			return self.GetValue("SomeString") as string;
		}

		public static void SomeIntValue(this MyTest self, int value)
		{
			self.SetValue("SomeIntValue", value);
		}

		public static int SomeIntValue(this MyTest self)
		{
			var result = self.GetValue("SomeIntValue");
			return result == null ? 0 : (int)result;
		}		
	}
}

Then mixin values can be stored/retrieved using type checked methods:

myClass.SomeString(avalue);
var val = myClass.SomeString();
var val = myClass.SomeString(null);
myClass.ClearAllMixins();

The core

The code below is all you need. If you take out the comments, it reduces to about 10 lines of code: a value store and four two line extension methods. Really, there’s not much to it.

The true heart is the ConditionalWeakTable variable which is used to store values. This class is a dictionary of values keyed by a System.Type. Class instances are the keys used. The ConditionalWeakTable will release the key and its values when the key instance goes out of scope.

Ordinarily the ConditionalWeakTable can hold only one value per key so a Dictionary is used as the value. This way any number of values, of any type, can be stored against an instance. I’ve chosen to put the extension methods in the ‘System’ namespace so they are available everywhere when the class is in a project.

namespace System
{
	public static class MixinExtensions
	{
		// A private store for mixin values
		static ConditionalWeakTable<object, Dictionary<string, object>> mixinsTable = new ConditionalWeakTable<object, Dictionary<string, object>>();

		/// <summary>
		/// Extension method will appear on every instance allowing arbitrary
		/// values to be recorded and will get the value for a name or null
		/// </summary>
		/// <param name="self">The calling instance</param>
		/// <param name="name">Name of the value to return</param>
		/// <returns></returns>
		public static object GetValue(this object self, string name)
		{
			var values = mixinsTable.GetOrCreateValue(self);
			return values.ContainsKey(name) ? values[name] : null;
		}

		/// <summary>
		/// Extension method will appear on every instance allowing arbitrary
		/// values to be stored.
		/// </summary>
		/// <param name="self">The calling instance</param>
		/// <param name="name">Name of the value to return</param>
		/// <param name="value">The value to save</param>
		public static void SetValue(this object self, string name, object value)
		{
			var values = mixinsTable.GetOrCreateValue(self);
			if (values.ContainsKey(name))
				values[name] = value; else
				values.Add(name, value);
		}

		/// <summary>
		/// Clear all mixin values for an instance
		/// </summary>
		/// <param name="self">The calling instance</param>
		public static void ClearAllMixins(this object self)
		{
			mixinsTable.GetOrCreateValue(self).Clear();
		}

		/// <summary>
		/// Remove a specific named value
		/// </summary>
		/// <param name="self">The calling instance</param>
		/// <param name="name">Name of the value to return</param>
		public static void DeleteValue(this object self, string name)
		{
			var values = mixinsTable.GetOrCreateValue(self);
			if (values.ContainsKey(name)) values.Remove(name);
		}
	}
}

Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

Be the first to leave a comment!