Click here to Skip to main content
Click here to Skip to main content

TypeBuilderLib, a Library to Build Dynamic Types Easily

, 23 Feb 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
TypeBuilderLib allows you to create dynamic types on the fly, which can increase the productivity of developers and the performance of applications.

Introduction

In this article I will discuss TypeBuilderLib, a library I wrote to easily emit types and manage (cache) them. Type emission is an alternative to code generation, having different advantages and inconveniences compared to it.

TypeBuilderLib comes with four types of type emitters out-of-the-box: Aspect Oriented Programming (AOP), Buffering, Indexer and Mask. Those type emitters are reference implementations of a generic framework to implement type emitters using the library.

The library also comes with a minimal suite of unit tests. I will use the code of the unit tests as examples of usage of the library in this article. Then I will explain the library architecture and how to extend it.

Background

The standard way to create a type is to code it: to write C# (or other .NET language) code defining the type and compile it into an assembly. This gives the programmer access to all the flexibility of programming language.

Some types are quite repetitive to create though and coding them can be error-prone or simply non-efficient and hard to maintain. A typical example is an adapter (see the wikipedia's article on the adapter pattern on adapter pattern): in order to deal with an object through a well-defined interface, you have to write a class wrapping that object and re-routing each call to the wrapped object.

What if we could simply define an interface and then ask a library to generate the adapter for us? This way we wouldn't have to write the adapters or maintain them.

This is exactly what TypeBuilderLib does.

Another way to solve the problem would be to generate the code for the adapters, using code generation tools such as Code Smith or MyGeneration. TypeBuilderLib uses type emission, which offers an alternative to code generation with the following advantages:

  • No code is generated, so no generated code needs to be managed or maintained. Depending on the problem this could mean that a substantial amount of code is eliminated.
  • With code generation, each time the model is changed, the code generation must be redone. TypeBuilderLib emits the type at execution time, so the types are always up to date.
  • It doesn't require installation of a tool in the developer environment.
  • Types can be emitted on the fly using information available only at runtime, which is impossible in the case of code generation.

The disadvantages of type emission are all related to the fact that type emission deals with Microsoft Intermediate Language (MSIL) code:

  • MSIL is a very low-level stack based language. A line of C# can easily be translated into 12 MSIL instructions. That makes writing MSIL a very tedious task.
  • When bad code is emitted the error is only apparent when the code is jitted and the error message is very generic. Visual debugging tools? Not really.

In practice, type emission can be used side-by-side with code-generation. This way, you can leverage the best tool for the task at hand.

Using the code

As mentioned in the introduction, TypeBuilderLib comes with four type emitters. We will look at examples of each of those.

Indexer

We will start with an example of an indexer. In this example, we want to access generic DataRows in a strongly type manner.

We first define the following interface:

public interface IPerson
{
    string Name { get;}
    int Age { get;set;}
}

In the following unit test, a variable table, of type DataTable, is pre-defined with a schema corresponding to the interface:

[Test]
public void TestSetAge()
{
   //    We create a row
   DataRow row = table.Rows.Add("Vincent", 33);
   //    We create an adapter for the row
   IPerson person = IndexerAdapterCache<IPerson>.GetInstance(row);

   Assert.AreEqual(row["Age"], person.Age);
   row["Age"] = 14;
   Assert.AreEqual(row["Age"], person.Age);
   person.Age = 5;
   Assert.AreEqual(row["Age"], 5);
}

On the first line, we create a DataRow. In the second line we call IndexerAdapterCache<IPerson>.GetInstance. This class belongs to TypeBuilderLib. The method GetInstance emits a class implementing the interface IPerson, and for each property of that interface, it implements call routing to the DataRow, casting the return value to the right type. GetInstance also caches the emitted type, so that if the same request is done again, the type won't be emitted a second time. The client code doesn't know about the generated type (since it didn't exist at compile time) - it simply handles a known interface. This is a pattern used throughout the library: emitted code implements a known interface.

The rest of the test simply tests that the Age property behaves normally and is actually getting and setting values in the data row.

IndexerAdapterCache<> can be used with any object exposing an indexer that takes a string as a parameter and returns any type of object (that is, any object obj where obj["string"] is valid), and any interface exposing properties only.

Typical uses are:

  • DataRow, as in the example
  • Any generic data structure provided by a framework such as:
    • Columns in a SharePoint list
    • Properties in a Commerce Server product or profile

Property mapping

By default, the emitted type uses the property name to call the underlying indexer object. For instance, in the previous example, the Name property would be emitted as:

string IPerson.Name
{
    get{return Indexer["Name"];}
}

In some cases it could be useful to have a property name not matching the index value. You can override this default behavior by using the attribute IndexerMapping. For instance:

public interface IPerson
{
    [IndexerMapping("name")]
    string Name { get;}
    [IndexerMapping("person_age")]
    int Age { get;set;}
}

Mask

The Mask type emitter is useful to implement traditional adapters. Let say we have the following interface:

public interface IPerson
{
    string Name { get;}
    int Age { get;set;}

    void GrowAge(int offset);
}

and a class not implementing that interface but having the same method signatures:

public class Person
{
   private readonly string name;
   private int age;

   public Person(string name, int age)
   {
      this.name = name;
      this.age = age;
   }

   public string Name
   {
      get { return name; }
   }

   public int Age
   {
      get { return age; }
      set { age = value; }
   }

   public void GrowAge(int offset)
   {
      age += offset;
   }
}

Let's say that this class is not under our control, so we cannot change it to add an interface implementation. The following unit test shows how to emit an adapter object bridging the class to be exposed as a given interface:

[Test]
public void TestPerson()
{
   //  Create a person object
   Person person = new Person("Vincent", 33);
   //  Emit an adapter exposing the person object as an IPerson
   IPerson personProxy = MaskAdapterCache<IPerson>
                                   .GetInstance<Person>(person);

   Assert.AreEqual(person.Age, personProxy.Age);
   personProxy.GrowAge(87);
   Assert.AreEqual(person.Age, personProxy.Age);
}

Typical uses are:

  • General objects outside your control (either part of another assembly or generated by Visual Studio Designer) that you want to access through a given interface.
  • The Profile object generated by ASP.NET 2.0 via a web.config configuration. This object is accessible only to classes within the web project. If you need to pass this profile to a component in another assembly, you can't. You could write an interface corresponding to the profile and pass a reference to an interface using an emitted adapter.

Buffering

The Buffering type emitter allows the creation of objects that can be used to buffer other objects.

For instance, let's say we have the following interface:

public interface IPerson
{
    string Name { get;}
    int Age { get;set;}
}

and an object Person implementing that interface. The following unit-test shows how to create a type that could buffer that object:

[Test]
public void TestAge()
{
   //    Create a person
   IPerson person = new Person("Vincent", 33);
   //    Buffer that person into a buffer object
   IPerson buffer = BufferingAdapterCache.GetReadOnlyInstance(person);

   Assert.AreEqual(person.Age, buffer.Age);
   ++person.Age;
   Assert.AreNotEqual(person.Age, buffer.Age);
}

The test shows that when we alter the original object (++person.Age), the buffered object stays unaltered.

Here we created a readonly buffer instance, but you can also create a buffer object that accept property sets. Readonly buffer object throws exception when a property set is called.

Now why wouldn't you buffer a Person object using another Person object? You should use this type emitter when the object you want to buffer cannot be kept around for a long period of time, e.g., because it holds a connection to a database.

Typical use are:

  • "Live objects", objects that can update themselves to a store via a Update method, e.g. list items in SharePoint.
  • Objects that do not have a copy method (such as Clone()).

AOP

There are a lot of libraries available implementing Aspect Oriented Programming (AOP, see wikipedia's article on AOP) in .NET. The AOP implementation of TypeBuilderLib isn't meant to compete with those. It is more a showcase of what the library can do.

AOP in TypeBuilderLib is implemented by wrapping objects with another object implementing the same interface and intercepting every call.

Let's take an interface:

public interface IPerson
{
   int Age { get;set;}
}

Let's say that we have a class Person implementing this interface. Let's define the following interceptor:

private class DetectInterceptor : IInterceptor
{
   private bool intercepted = false;

   public bool Intercepted
   {
      get { return intercepted; }
   }

   #region IInterceptor Members
   object IInterceptor.InterceptCall(IMethodInvoker invoker)
   {
      intercepted = true;

      return invoker.Invoke();
   }
   #endregion
}

What this object does is to implement the IInterceptor interface and simply marks the intercepted field as true when a method or property is called. That is not a very useful interceptor, but it allows us to test the library:

[Test]
public void TestPropertyGet()
{
   //    Create an object on which we want to attach some aspect
   IPerson person = new Person(33);
   //    Create an interceptor
   DetectInterceptor interceptor = new DetectInterceptor();
   //    Create the proxy object intercepting calls to the real object
   IPerson proxy = AopAdapterCache
                            .GetInstance<IPerson>(person, interceptor);

   Assert.IsFalse(interceptor.Intercepted);

   //    Do a call to be intercepted
   int age = proxy.Age;

   Assert.AreEqual(person.Age, age);
   Assert.IsTrue(interceptor.Intercepted);
}

The test goes as follows: after attaching an interceptor to an object, we check that the interceptor hasn't been called, then we do a get on the Age property and check that the returned age is correct (the same than the value returned by the real object) and that the interceptor has been called.

Typical uses are:

  • Logging of method calls
  • Consistent exception handling mechanisms on objects
  • Security layer on method calls

Converters

In many of the sample type emitters we might want to have some control over the type emission. The way for the library to let us jump in and control the type emission is to put attributes on members. Here we will talk about the converters and the attribute TypeConverterAttribute.

A classic example is an indexer type emitter, where for a given property, the interface property type is an enum and the underlying object exposes an int. This is quite typical when the underlying object takes an object from a database. This would be the way to decorate the interface:

public interface IDemand
{
    [TypeConverter(typeof(EnumConverter<Status>))]
    Status DemandStatus { get;}
}

You can pass a list of type converters to the TypeConverter attribute, each converts the underlying object value to another value and pass it to the next converter. Here we just use one. This converter is provided out-of-the-box, but you can develop your own by implementing ITypeConverter<F, T>.

Typical use are:

  • Converting strings or integers values to an enum.
  • Converting a DBNullable value to a nullable value (using Nullable<T> for value types or just null for reference types).
  • Any type of value and/or type conversions.

Architecture of TypeBuilderLib

TypeBuilderLib is built upon the type emission capabilities of the .NET Framework, found in System.Reflection.Emit. TypeBuilderLib is meant to:

  • Ease type emission development
  • Cache emitted types

This following UML diagram shows the most important types in the library: TypeEmitterCache and TypeEmitterBase. Note that I extended UML with the '#' symbol to show protected scope. I also color-coded the classes depending on their stereotypes.

Basic types in the library

TypeEmitterCache has two methods: GetInstance and GetDynamicType. GetDynamicType looks up for a type in the cache. If it can't find it, it asks the type emitter to create one. GetInstance calls GetDynamicType and instantiates the type. The primary function of TypeEmitterCache is therefore to coordinate the creation of types to make sure that the same type doesn't get created twice, for performance reasons.

TypeEmitterBase is where all the logic of type emission is. This logic is twofold:

  • Representing a type to be created. It can be used as a key in a dictionary. Every class derived fromTypeEmitterBase overrides the object type methods Equals and GetHashCode.
  • Ability to emit a type with a System.Reflection.Emit.ModuleBuilder. This is done by overriding the method EmitType.

Each instance of an TypeEmitterBase represents a recipe to emit a type. The following diagram shows the hierarchy of type emitters implemented in TypeBuilderLib.

Type emitters hierarchy

TypeEmitterBase being a rather large contract, a specialization was made: TypeEmitterOneInterfaceBase. This specialization emits types implementing one and only one interface. That is the case of the four emitters implemented in the library. TypeEmitterOneInterfaceParamBaseClassBase is a further specialization allowing parameterization of the base type of the emitted types.

Each implementation of TypeEmitterBase must implement the emission of a type, which means emitting MSIL. This is a non-trivial and frustrating task. Therefore, we want to limit the amount of logic we emit. A good strategy for doing that is to put most of the logic in a base type that we write in C#, derive the emitted type from this base type, and call the method of the base type. This way, we only have calls to emit - no complicated logic (e.g. if, then, else, switch, computation, etc.). TypeEmitterOneInterfaceParamBaseClassBase comes into play to implement that strategy, allowing parameterization of the type emitter with the base type it's using.

In the code sample, we have never seen calls to TypeEmitterCache. This is because we were always using a facade to TypeEmitterCache. The following diagram shows the facades of TypeEmitterCache provided in TypeBuilderLib.

Different facade of TypeEmitterCache provided by the library

Each cache facade uses TypeEmitterCache and none exposes TypeEmitterBase in its interface. TypeEmitterBase is used internally when a method of a facade is called: an instance of a TypeEmitterBase is created, parameters are set on it, and the EmitType method is called on it.

Performance

Type emission is very efficient. We tested the creation of an AOP proxy and it takes around 15 miliseconds. Of course, this penalty happens only the first time a given type is created. The next calls for the same type simply take the type from the cache.

How to extend TypeBuilderLib

To extend TypeBuilderLib, you need to implement three components:

  • A type emitter to emit a type.
  • An emitter cache facade to create and manage type emitters.
  • A base class with the basic logic of the type you want to emit.

Conclusion

We have discussed TypeBuilderLib, a library I wrote to easily emit types and manage (cache) them. This library is useful out-of-the-box with the type emitters it provides (AOP, Buffering, Indexer and Mask) or as a framework to develop new type emitters.

A good way to understand the library is to look at the unit tests.

Using type emission can be a great alternative to code generation when information available only at runtime is required to emit types, or just to simplify project maintenance by eliminating a code generation phase in the build process.

License

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

Share

About the Author

Vincent-Philippe Lauzon
Architect CGI
Canada Canada
Vincent-Philippe is a Senior Solution Architect working in Montreal (Quebec, Canada).
 
His main interests are Windows Azure, .NET Enterprise suite (e.g. SharePoint 2013, Biztalk Server 2010) & the new .NET 4.5 platforms.

Comments and Discussions

 
QuestionRevisiting Type Builder Lib? PinmemberVincent-Philippe Lauzon27-Aug-09 7:36 
GeneralClone Implementation - Undo Pinmembertwesterd25-Sep-07 22:29 
GeneralRe: Clone Implementation - Undo PinmemberVincent-Philippe Lauzon26-Sep-07 4:43 
GeneralRe: Clone Implementation - Undo Pinmembertwesterd26-Sep-07 9:06 
GeneralInteresting PinmemberBartek G.26-Feb-07 23:44 
This is all very interesting. I hope I have some time to try it later.
 
I also want to comment on the following phrase:
 
Types can be emitted on the fly using information available only at runtime, which is impossible in the case of code generation.
 
Actually this is not exactly true. You can generate code at runtime, compile it and use compiled assembly later.
 
Have a look at the Microsoft.CSharp namespace.
 
Best regards,
Bartek
GeneralRe: Interesting PinmemberVincent-Philippe Lauzon27-Feb-07 3:32 
GeneralRe: Interesting PinmemberBartek G.28-Feb-07 4:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.141015.1 | Last Updated 23 Feb 2007
Article Copyright 2007 by Vincent-Philippe Lauzon
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid