Static Mixins in .Net

Simon Cropp and I are working on a tool to add multiple inheritance support to the .net framework as a post build task (msbuild) using the glorious Mono.Cecil library. The project is hosted here.

Why multiple inheritance?

We are not interested in Multiple Inheritance as such, but in one specific case: Mixins. Mixins are an special case of multiple inheritance, where the inheritance doesn’t mean specialization as in single inheritance but means adding an specific functionality.

  • A simple way to think of mixins is as “an interface with implementation”. If you have written Java interfaces, you have surely found yourself writing the same implementations over and over for each class that implements the interface. A mixin lets you write the implementation once and have it automatically included in each class that uses it.

  • Mixins work best when they are small, independent units of functionality, orthogonal to other classes and mixins. If your mixins obey this guideline, then the model of how mixins work is very simple: it is as if the body of each mixin were copied textually into each class that uses it.  From OpenLaszlo site.

This concept is widely used in other languages such as Simula, Smalltalk, Perl, Javascript, Ruby and Python (more languages here).

Why static?

We know about other tools for creating mixins dynamically like Castle DynamicProxy, Spring.Net and LinFu. These tools work by creating a proxy type on runtime, and redirecting calls to each target or mixed instance. This work well for some use cases, but we found it doesn’t in others:

  • Sometimes you don’t own the activation of the instance. E.g. you want to use mixin with an entity but you don’t have control on the instantiation of the entity when it come from an ORM tool.
  • Type hell: lets say that you have a validation framework, configured to validate an specific class: Person. When working with dynamic proxies, you have an instance of PersonProxy instead of Person, so you will need to tell your validation framework somehow that PersonProxy must be validated as a Person.
  • Reflection-heavy scenarios like WPF sometimes fail.

How it works?

//Simple templates

public class Auditable : IAuditable
{
    public DateTime CreateDate { get; set; }
    public string CreatedBy { get; set; }
    public DateTime UpdateDate { get; set; }
    public string UpdateBy { get; set; }
}


public class ErrorInfo : IDataErrorInfo
{
    public object Target { get; set; }

    #region IDataErrorInfo Members

    public string this[string columName]
    {
        get { return Validator.Validate(Target, columName); }
    }

    public string Error
    {
        get { return Validator.Validate(Target); }
    }

    #endregion
}

//Your code

[ExtendWith(typeof(ErrorInfo), typeof(Auditable))]
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime Birthdate { get; set; }
}

//What gets compiled

public class Person : IAuditable, IDataErrorInfo
{
    private Auditable Auditable_1 = new Auditable();
    private ErrorInfo ErrorInfo_1 = new ErrorInfo();

    public Person()
    {
        this.ErrorInfo_1.Target = this;
    }

    public DateTime Birthdate { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
        
    //Auditable members
        
    public DateTime CreateDate
    {
        get
        {
            return this.Auditable_1.CreateDate;
        }
        set
        {
            this.Auditable_1.CreateDate = value;
        }
    }

    public string CreatedBy
    {
        get
        {
            return this.Auditable_1.CreatedBy;
        }
        set
        {
            this.Auditable_1.CreatedBy = value;
        }
    }
        
    public string UpdateBy
    {

        get
        {
            return this.Auditable_1.UpdateBy;
        }

        set
        {
            this.Auditable_1.UpdateBy = value;
        }
    }

    public DateTime UpdateDate
    {
        get
        {
            return this.Auditable_1.UpdateDate;
        }
        set
        {
            this.Auditable_1.UpdateDate = value;
        }
    }

    //dataerror info members
        
    public string Error
    {
        get
        {
            return this.ErrorInfo_1.Error;
        }
    }

    public string this[string columName]
    {
        get
        {
            return this.ErrorInfo_1[columName];
        }
    }
}

This is the current implementation now. In the beginning we though about cloning instruction per instruction but it is really hard when it comes to generics templates. Also with this approach you can debug mixed templates.

Simple configuration

You only need to add these few lines to the end of your project file.

2011-02-01_0837

No reference required

Just add the attribute to your project, it can be internal and that’s all. Heredar will find the attribute by name. Otherwise, you can add a reference to Heredar.dll (there is a version for standard .net, Silverlight and phone) use the attribute, and Heredar will remove the attributes and the reference after weaving.


blog comments powered by Disqus
  • Categories

  • Archives