Click here to Skip to main content
15,895,746 members
Articles / Programming Languages / C#

GOF's Adapters and Some Magic

Rate me:
Please Sign up or sign in to vote.
3.80/5 (3 votes)
21 Apr 2009CPOL14 min read 25.1K   85   32  
How to create automated Adapters and extend/modify the original functionality with default member’s implementation for interfaces, Eiffel-like Separates, Code Injection, and Design by Contract.
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;

namespace Scaredfingers.Adapters
{
  public interface IInjection
  {
    MethodInfo InjectionSource { get; set; }
    Type ResolutionContext { get; set; }
    IList<Assembly> References { get; }

    void Inject(ILGenerator generator, MethodBuilder method, TypeBuilder container);
  }

  public abstract class Injection : IInjection
  {
    #region IInjection Members
    protected MethodInfo mth_InjectionSource;
    public MethodInfo InjectionSource
    {
      get
      {
        return mth_InjectionSource;
      }
      set
      {
        mth_InjectionSource = value;
      }
    }

    protected Type typ_ResolutionContext;
    public Type ResolutionContext
    {
      get
      {
        return typ_ResolutionContext;
      }
      set
      {
        typ_ResolutionContext = value ;
      }
    }

    protected IList<Assembly> lst_References = new List<Assembly>();
    public IList<Assembly> References
    {
      get { return lst_References; }
    }

    public abstract void Inject(ILGenerator generator, MethodBuilder method, TypeBuilder container) ;
    #endregion
  }

  public interface IInjectionAttribute
  {
    IInjection Injection { get; }
  }

  public interface IPreInjectionAttribute
  {
    IInjection Injection { get; }
  }

  public interface IPostInjectionAttribute
  {
    IInjection Injection { get; }
  }

  public class InjectionsAdapterBuilder : AdapterBuilder
  {
    #region Creation
    public InjectionsAdapterBuilder(string assemblyName)
      : base(assemblyName)
    {

    }

    public InjectionsAdapterBuilder()
    {

    }
    #endregion

    #region Overrides
    protected override void EmitMethodBody(ILGenerator generator, FieldBuilder adapteesReferenceField, MethodBuilder methodBuilder,
      MethodInfo adapteesMethod, MethodInfo prototypeMethod, Type adapterInterfaceType, Type adapteeType, TypeBuilder adapterTypeBuilder)
    {
      foreach (IInjection injection in GetPreInjections(adapteesReferenceField, methodBuilder, adapteesMethod, prototypeMethod,
        adapterInterfaceType, adapteeType, adapterTypeBuilder))
        injection.Inject(generator, methodBuilder, adapterTypeBuilder);

      EmitMethodUninjectedMethodBody(generator, adapteesReferenceField, methodBuilder, adapteesMethod, prototypeMethod,
        adapterInterfaceType, adapteeType, adapterTypeBuilder);

      foreach (IInjection injection in GetPostInjections(adapteesReferenceField, methodBuilder, adapteesMethod, prototypeMethod,
        adapterInterfaceType, adapteeType, adapterTypeBuilder))
        injection.Inject(generator, methodBuilder, adapterTypeBuilder);
    }

    protected virtual void EmitMethodUninjectedMethodBody(ILGenerator generator, FieldBuilder adapteesReferenceField, MethodBuilder methodBuilder,
      MethodInfo adapteesMethod, MethodInfo prototypeMethod, Type adapterInterfaceType, Type adapteeType, TypeBuilder adapterTypeBuilder)
    {
      base.EmitMethodBody(generator, adapteesReferenceField, methodBuilder, adapteesMethod, prototypeMethod,
        adapterInterfaceType, adapteeType, adapterTypeBuilder);
    }
    #endregion

    #region Virtuals
    protected virtual IList<IInjection> GetPreInjections(FieldBuilder adapteesReferenceField, MethodBuilder methodBuilder,
      MethodInfo adapteesMethod, MethodInfo prototypeMethod, Type adapterInterfaceType, Type adapteeType, TypeBuilder adapterTypeBuilder)
    {
      return GetInjections(typeof(IPreInjectionAttribute), adapteesReferenceField, methodBuilder,
        adapteesMethod, prototypeMethod, adapterInterfaceType, adapteeType, adapterTypeBuilder);
    }

    protected virtual IList<IInjection> GetPostInjections(FieldBuilder adapteesReferenceField, MethodBuilder methodBuilder,
      MethodInfo adapteesMethod, MethodInfo prototypeMethod, Type adapterInterfaceType, Type adapteeType, TypeBuilder adapterTypeBuilder)
    {
      return GetInjections(typeof(IPostInjectionAttribute), adapteesReferenceField, methodBuilder,
        adapteesMethod, prototypeMethod, adapterInterfaceType, adapteeType, adapterTypeBuilder);
    }

    protected static readonly AdapterBuilder bdr_Injections = new AdapterBuilder();
    protected virtual IList<IInjection> GetInjections(Type injectionsAttributeType, FieldBuilder adapteesReferenceField, MethodBuilder methodBuilder,
      MethodInfo adapteesMethod, MethodInfo prototypeMethod, Type adapterInterfaceType, Type adapteeType, TypeBuilder adapterTypeBuilder)
    {
      List<IInjection> lst_Result = new List<IInjection>();
      foreach (object att_Injection in prototypeMethod.GetCustomAttributes(injectionsAttributeType, true))
      {
        IInjection inj_Injection = ((IInjectionAttribute)bdr_Injections.BindAdapter(typeof(IInjectionAttribute), injectionsAttributeType, 
          att_Injection)).Injection;
        inj_Injection.InjectionSource = prototypeMethod;
        inj_Injection.ResolutionContext = adapterInterfaceType;

        lst_Result.Add(inj_Injection);
      }

      foreach (object att_Injection in adapterInterfaceType.GetCustomAttributes(injectionsAttributeType, true))
      {
        IInjection inj_Injection = ((IInjectionAttribute)bdr_Injections.BindAdapter(typeof(IInjectionAttribute), injectionsAttributeType, 
          att_Injection)).Injection;
        inj_Injection.InjectionSource = prototypeMethod;
        inj_Injection.ResolutionContext = adapterInterfaceType;

        lst_Result.Add(inj_Injection);
      }

      return lst_Result;
    }
    #endregion
  }

  public abstract class InjectionAttribute : Attribute, IInjectionAttribute
  {
    #region IInjectionAttribute Members

    public abstract IInjection Injection
    {
      get;
    }

    #endregion
  }

  public class LogAttribute : Attribute, IPreInjectionAttribute, IPostInjectionAttribute
  {
    #region IInjectionAttribute Members

    IInjection IPreInjectionAttribute.Injection
    {
      get { return new LogEnter(); }
    }

    #endregion

    #region IPostInjectionAttribute Members

    IInjection IPostInjectionAttribute.Injection
    {
      get { return new LogExit() ; }
    }

    #endregion

    #region Nested Injections
    public abstract class LogInjection : Injection
    {
      protected string SignatureToString(MethodInfo method)
      {
        return string.Format("{0} {1}::{2}({3})", method.ReturnType.FullName, method.ReflectedType.FullName, 
          method.Name, ParametersToString(method.GetParameters()));
      }

      protected string ParametersToString(ParameterInfo[] formalParameters)
      {
        string result = "";
        foreach (ParameterInfo param in formalParameters)
          result += "," + param.ParameterType.FullName;

        return result.Length > 0? result.Substring(1) : "";
      }
    }

    public class LogEnter : LogInjection
    {
      public override void Inject(ILGenerator generator, MethodBuilder method, TypeBuilder container)
      {
        generator.EmitWriteLine("[Enter]" + SignatureToString(InjectionSource));
      }
    }

    public class LogExit : LogInjection
    {
      public override void Inject(ILGenerator generator, MethodBuilder method, TypeBuilder container)
      {
        generator.EmitWriteLine("[Exit]" + SignatureToString(InjectionSource));
      }
    }
    #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
Architect SunHotels
Spain Spain
I Received a Bachelor's Degree in Computer Science at the Mathematics and Computer Science Faculty, University of Havana, Cuba.

I mainly work in web applications using C# and some Javascript. Some very few times do some Java.

Comments and Discussions