Introduction

I wanted to experiment with using .NET's TypeBuilder class to automatically generate classes at runtime. For my experiment, I decided to implement a function, that, given an interface, returns a fully functional object that implements the interface. The programmer does not have to create a class to implement the interface.

In this article, I will first describe how one uses the automatic interface implementer. Later, I will describe how I used reflection and .NET's TypeBuilder to, at runtime, dynamically create Types that implement interfaces.

Using the Automatic Interface Implementer

Using the Automatic Interface Implementer requires a pre-existing interface and a single line of code:

That's it! The Automatic Interface Implementer will return a working object for you to use without you having to write a class to implement the interface. This could be a great time-saver when writing a program that makes heavy use of interfaces.

There are some things to keep in mind when using the Automatic Interface Implementer:

Properties will work, provided the interface declares both a getter and a setter. (This is what the above example does.) Properties that only have a getter will either return a default value or null. Properties that only have a setter are essentially no-ops.

Methods will do nothing.

If the method has a return value, it will return a default value. This is usually 0, false, or null.

The interface implementer is tested with properties, methods, generic interfaces, and interfaces that inherit from other interfaces. It is not tested with any other unanticipated features of .NET.

The interface implementer will throw an exception if it is used with a class.

When the interface implementer is part of a referenced module, it will only work with public interfaces. To use internal interfaces, include it within a module.

The performance implications of using the interface implementer are minimal. At runtime, there will be a minor performance hit the first time it is used with an interface. (This is because it needs to generate a type for use with the interface.) Each subsequent use with the same interface will use the same type. Thus, using the interface implementer to build many objects of the same type will be almost as fast as creating a compiled class for the interface.

Think carefully about using the interface implementer in production code. It has not been tested under reduced security contexts. It is intended for rapid application development.

How it Works

I will now describe, step-by-step, how the interface implementer works.

Introduction: Type Objects

A powerful feature of .NET is that all types can be represented as objects at runtime. The Type class is used for this purpose. Type objects can be passed as arguments to functions, inspected using Reflection, and used to create an object of the represented type.

The following example creates Type objects for different classes and primitives:

The typeof() operator returns a Type object for a given type. All objects also have a GetType() method which returns the Type object, although that is beyond the scope of this article.

The InterfaceObjectFactory Class

The InterfaceObjectFactory is static. This means that it's possible to use its methods without instantiating an instance of the class. Keeping the class static saves memory and CPU cycles as all references to the class will use the same generated types.

A Dictionary is used to keep track of generated types, as follows:

///<spanclass="code-SummaryComment"><summary></span>/// All of the types generated for each interface.
/// This dictionary is indexed by the interface's type object
///<spanclass="code-SummaryComment"></summary></span>privatestatic Dictionary<Type, Type> InterfaceImplementations =
new Dictionary<Type, Type>();

The Dictionary is indexed by interfaces; specifically, the interfaces' Type objects index it. In the dictionary, each interface will have a generated Type object that implements the interface.

Stepping into the New() method

The New() method, a generic method, is deliberately kept simple. It relies on the dictionary to have a type that implements the used interface. If the type does not exist, it calls a method to implement it.

///<spanclass="code-SummaryComment"><summary></span>/// Returns an object that implement an interface, without the need to
/// manually create a type that implements the interface
///<spanclass="code-SummaryComment"></summary></span>///<spanclass="code-SummaryComment"><typeparamname="T">T must be an interface that is public.</typeparam></span>///<spanclass="code-SummaryComment"><returns>An object that implements the T interface</returns></span>///<spanclass="code-SummaryComment"><exceptioncref="TypeIsNotAnInterface">Thrown if T is not an interface</span>///<spanclass="code-SummaryComment"></exception></span>publicstatic T New<T>()
where T:class
{
Type type = typeof(T);
// If the type that implements the isn't created, create itif (!InterfaceImplementations.ContainsKey(type))
CreateTypeFor(type);
// Now that the type exists to implement the interface, // use the Activator to create an objectreturn (T)Activator.CreateInstance(InterfaceImplementations[type]);
}

This method is generic. It allows the caller to use the method without performing any type-casting. The where syntax is used to instruct the compiler to reject primitive types like int, long, bool, etc. Unfortunately, we cannot instruct the compiler to reject classes; the CreateTypeFor() method will throw an exception if it is not passed an interface, which we will just pass to the caller.

The first thing the method does is load a Type object for the type specified in the generic argument. It then checks to see if the type is not in the dictionary, if it isn't, it calls the type generation method: CreateTypeFor().

After calling CreateTypeFor(), we are sure that there is a Type object present in the dictionary, or an exception is thrown. We then use the Activator static class to generate an object that implements the interface, which is returned to the caller.

Stepping into CreateTypeFor()

CreateTypeFor() is called to create a class, as a Type object, to implement an interface. The first thing that it does is verify that the type is an interface:

if (!type.IsInterface)
thrownew TypeIsNotAnInterface(type);

Fortunately, the above code is the only error checking we need to perform!

Next, we create a TypeBuilder object that provides the functionality for building the class. We will give our class a name, make it public, specify that it's a class, and specify the interface that it implements:

In .NET, all classes need a constructor, including ones dynamically generated at run time. In order to be compatible with the Activator, our constructor will take no arguments. All it will do is call the base class' constructor.

With the constructor declared, it is time to add our first op-codes to the class. Op-codes are similar to assembly, so anyone who has worked with assembly will probably feel that this is very familiar. We will ask the ConstructorBuilder object to give us an ILGenerator object, which we use to input the op-codes.

After building the constructor, we will need to obtain a list of methods to implement. Even though we will implement the interface's properties before we implement the methods, we get the methods first because all properties' getters and setters are also returned as methods. By persisting the list of methods while we implement the properties, we remove the properties' methods from the methods list.

Now that we know the properties, it's time to implement them. We will iterate through each property using a foreach. Each property will have a field named after the property, with an underscore as the name's prefix. If the property has a getter, we will implement it using a MethodBuilder and an IlGenerator in a similar way to how we generated the constructor. Likewise, if the property has a setter, we will also use a MethodBuilder and IlGenerator to implement the setter.

The op-codes for the getter will load the object and then the field onto the stack, and return it. The op-codes for the setter will load the object and the argument, "value" onto the stack, and then set the field. In both cases, the new method will need to be associated with the interface's getter or setter.

foreach (PropertyInfo pi in properties)
{
string piName = pi.Name;
Type propertyType = pi.PropertyType;
// Create underlying field; all properties have a field of the same type
FieldBuilder field =
typeBuilder.DefineField("_" + piName, propertyType, FieldAttributes.Private);
// If there is a getter in the interface, create a getter in the new type
MethodInfo getMethod = pi.GetGetMethod();
if (null != getMethod)
{
// This will prevent us from creating a default method for the property's getter
methods.Remove(getMethod);
// Now we will generate the getter method
MethodBuilder methodBuilder = typeBuilder.DefineMethod(getMethod.Name,
MethodAttributes.Public | MethodAttributes.Virtual, propertyType,
Type.EmptyTypes);
// The ILGenerator class is used to put op-codes (similar to assembly) // into the method
ilGenerator = methodBuilder.GetILGenerator();
// These are the op-codes, (similar to assembly)
ilGenerator.Emit(OpCodes.Ldarg_0); // Load "this"// Load the property's underlying field onto the stack
ilGenerator.Emit(OpCodes.Ldfld, field);
ilGenerator.Emit(OpCodes.Ret); // Return the value on the stack// We need to associate our new type's method with the // getter method in the interface
typeBuilder.DefineMethodOverride(methodBuilder, getMethod);
}
// If there is a setter in the interface, create a setter in the new type
MethodInfo setMethod = pi.GetSetMethod();
if (null != setMethod)
{
// This will prevent us from creating a default method for the property's setter
methods.Remove(setMethod);
// Now we will generate the setter method
MethodBuilder methodBuilder = typeBuilder.DefineMethod
(setMethod.Name, MethodAttributes.Public |
MethodAttributes.Virtual, typeof(void), new Type[] { pi.PropertyType });
// The ILGenerator class is used to put op-codes (similar to assembly) // into the method
ilGenerator = methodBuilder.GetILGenerator();
// These are the op-codes, (similar to assembly)
ilGenerator.Emit(OpCodes.Ldarg_0); // Load "this"
ilGenerator.Emit(OpCodes.Ldarg_1); // Load "value" onto the stack// Set the field equal to the "value" on the stack
ilGenerator.Emit(OpCodes.Stfld, field);
ilGenerator.Emit(OpCodes.Ret); // Return nothing// We need to associate our new type's method with the // setter method in the interface
typeBuilder.DefineMethodOverride(methodBuilder, setMethod);
}
}

Now that the getters and setters are created, the last complex step is to generate the "no-op" methods. For each method, we will get a list of arguments and a return type. This is used to generate a MethodBuilder and IlGenerator object, which are used in the same way that we created the constructor, getters, and setters.

On building the op-codes, if there is a return type, the method will declare an un-initialized field of the return type, and load it onto the stack. The method will return and it will be associated with the method as declared in the interface.

Now that the constructor, properties, and methods are generated, it is time to complete the class. This requires us to tell the TypeBuilder that "we're done" by calling the CreateType() method, and it requires us to store the newly-created class for later use.

So that's it! Our newly-created class that implements an interface is ready for InterfaceObjectFactory.New<>() to return! The newly created type is placed into the InterfaceImplementations dictionary, just as InterfaceObjectFactory.New<>() expects.

Building and Running the Provided Source Code

The provided source code is two projects and a solution for Visual Studio 2005. The programmer is encouraged to integrate InterfaceObjectFactory.cs into an existing class. (It may be useful to change the namespace.)

The demo program is a unit test, Memmexx.InterfaceImplementor.UnitTests. It is built to run under NUnit, an industry-standard testing Framework. As the demo uses basic NUnit functionality, it should run under most old and new versions of NUnit. The demo will look for nunit.core.dll and nunit.framework.dll in C:\Program Files\NUnit 2.4.3\bin. Assuming that you have a different version of NUnit or have NUnit installed in a different location, you will need to remove and re-add the references to nunit.core.dll and nunit.framework.dll.

Comments and Discussions

I actually started using these techniques for accessing the Mongo database in C#. (Mongo is a NoSQL database that stores JSON-like documents and allows for queries on them.) My wrapper that uses these techniques is here: http://bitbucket.org/gwbasic/mongodb.emitter/[^]

If your interface contains any indexed properties, you'll get a TypeLoadException when you try to implement it: Signature of the body and declaration in a method implementation do not match.

The easiest way around this is to ignore any index parameters, and store the value in the same backing field for every index. You'll need to pass the parameter types for the get and set methods, and change the index of the value parameter for the set method to be the last parameter.

Also, there's no point in defining the backing field for a write-only property, as the field value can never be used.

"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer