Reader Level:
ARTICLE

How to create mixin using C# 4.0

Posted by Vulpes Articles | C# Language March 07, 2011
A mixin is a class which adds functionality to other classes but which cannot itself be instantiated.
  • 0
  • 0
  • 11804

What's a mixin and why is it useful?

A mixin is a class which adds functionality to other classes but which cannot itself be instantiated.
Mixins are useful in object oriented languages which only support single inheritance as a sort of 'half way house' between single and multiple inheritance.  

If C# only supports single inheritance how can it implement mixins?

It can do so using a combination of interfaces and extension methods. 

In order to implement a mixin a class must first implement an interface that has been created for that mixin.  This interface may include members which the class has to implement itself or can be completely empty!

As a class can implement multiple interfaces, this does not affect any interfaces which it already implements.

A static class containing extension methods then provides the mixin's actual methods which can be applied to any object which implements the associated interface. 

Does this mean that mixins can only have methods - what about fields?

No, they can have fields as well.

The trick here is to define a nested class within the mixin which contains the fields and then associate an instance of this nested class with each object which implements the mixin.

Won't that prevent the object from being garbage collected when it's no longer used?

Not if we use 'weak references'. These are references to an object which the garbage collector ignores when deciding whether an object can be collected.

But how do you get rid of the weak references themselves?

Prior to C# 4.0, you had to clean up the weak references manually which was quite messy.

All this has changed with the introduction of the ConditionalWeakTable class in .NET Framework 4.0. When the object is garbage collected, the weak reference is automatically removed from the table together with the reference to the associated fields. The instance of the nested class can then be garbage collected normally.
Better still, the ConditionalWeakTable is thead-safe and so no additional locking is required if you're using mixins in a multi-threaded application. 

Sounds interesting but what about some code?

Here's an example where we add the ability to calculate a current age to any class which needs it.

using System;
using System.Runtime.CompilerServices; //needed for ConditionalWeakTable
public interface MAgeProvider // use 'M' prefix to indicate mixin interface
{
    // nothing needed in here, it's just a 'marker' interface
}
public static class AgeProvider // implements the mixin using extensions methods
{
    static ConditionalWeakTable<MAgeProvider, Fields> table;
    static AgeProvider()
    {
        table = new ConditionalWeakTable<MAgeProvider, Fields>();
    }
    private sealed class Fields // mixin's fields held in private nested class
    {
        internal DateTime BirthDate = DateTime.UtcNow;
    }
    public static int GetAge(this MAgeProvider map)
    {
        DateTime dtNow = DateTime.UtcNow;
        DateTime dtBorn = table.GetOrCreateValue(map).BirthDate;
        int age = ((dtNow.Year - dtBorn.Year) * 372
                   + (dtNow.Month - dtBorn.Month) * 31
                   + (dtNow.Day - dtBorn.Day)) / 372;
        return age;
    }
    public static void SetBirthDate(this MAgeProvider map, DateTime birthDate)
    {
        table.GetOrCreateValue(map).BirthDate = birthDate;
    }
}

public abstract class Animal
{
    // contents unimportant
}
public class Human : Animal, MAgeProvider
{
    public string Name;
    public Human(string name)
    {
        Name = name;
    }
    // nothing needed in here to implement MAgeProvider
}
static class Test
{
    static void Main()
    {
        Human h = new Human("Jim");
        h.SetBirthDate(new DateTime(1980, 1, 1));
        Console.WriteLine("Name {0}, Age = {1}", h.Name, h.GetAge());
        Human h2 = new Human("Fred");
        h2.SetBirthDate(new DateTime(1960, 6, 1));
        Console.WriteLine("Name {0}, Age = {1}", h2.Name, h2.GetAge());
        Console.ReadKey();
    }
}

When you build and run this program, the output should be:

Name Jim, Age = 31
Name Fred, Age = 50

Is Microsoft likely to add language support for mixins to C# in future?

Judging by their response to a recent suggestion on Microsoft Connect , not anytime soon though they didn't rule them out completely.

The above example is a workaround I posted to that suggestion. The code would be nicer if we had 'extension properties'  as well as extension methods which I do think is a possibility in the next version as it's a feature which is often requested.

In the meantime, we can do quite a bit with what we have so, if I've fired your imagination,  have fun!

COMMENT USING

Trending up