Implementing a class at runtime with Reflection.Emit

This is a simple example of how an interface can by dynamically implemented at runtime using the System.Reflection.Emit namespace. We’ll start by defining a dynamic assembly, then defining a class which implements our test interface. This is how frameworks, like Moq, can create interface implementations for you, which is especially powerful for unit testing.

Here is the interface we will be working with.

/// <summary>
/// The test interface to implement.
/// </summary>
/// <remarks>
/// Make this public so that the new assembly can see it.
/// Or set the "InternalsVisibleTo" attribute on this assembly with the name of the new dynamic assembly.
/// </remarks>
public interface ITestInterface
{
    /// <summary>
    /// This method will be implemented as a no-op.
    /// </summary>
    void ImplementMeToDoNothing();

    /// <summary>
    /// This method will return a default integer.
    /// </summary>
    /// <returns>Zero.</returns>
    int ImplementMeToReturnDefaultInt();

    /// <summary>
    /// This method will throw a NotImplementedException.
    /// </summary>
    void ImplementMeToThrowAnException();

    /// <summary>
    /// This method will return the value argument.
    /// </summary>
    /// <param name="argument">The return value.</param>
    /// <returns>The argument.</returns>
    int ImplementMeToReturnMyArgument(int argument);
}

Here are some test method to better explain our goals.

[TestMethod]
public void EnsureDoNothingSucceeds()
{
    ITestInterface instance = CreateMock();
    instance.ImplementMeToDoNothing();
}

[TestMethod]
public void EnsureDefaultReturnsZero()
{
    ITestInterface instance = CreateMock();
    Assert.AreEqual(0, instance.ImplementMeToReturnDefaultInt());
}

[TestMethod]
public void EnsureThrowsNotImplementedException()
{
    ITestInterface instance = CreateMock();
    Assert.ThrowsException<NotImplementedException>(() => instance.ImplementMeToThrowAnException());
}

[TestMethod]
public void EnsureReturnsArgument()
{
    ITestInterface instance = CreateMock();
    Assert.AreEqual(12345, instance.ImplementMeToReturnMyArgument(12345));
}

So lets define the function which will create our mock. This will build a new assembly, and create a class which implements ITestInterface.

private static ITestInterface CreateMock()
{
    // Create a dynamic assembly and module.
    var assemblyName = new AssemblyName("TestAssembly");
    var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(name: assemblyName, access: AssemblyBuilderAccess.RunAndCollect);
    var moduleBuilder = assemblyBuilder.DefineDynamicModule(name: assemblyName.Name);

    // Define the class builder for the new type to implement the interface "ITestInterface"
    var classBuilder = moduleBuilder.DefineType(
        name: nameof(ITestInterface) + "_Implementation",
        attr: TypeAttributes.Class | TypeAttributes.Public,
        parent: typeof(object),
        interfaces: new[] { typeof(ITestInterface) });

    // We don't have anything special to do in the constructor, so define a default one.
    classBuilder.DefineDefaultConstructor(MethodAttributes.Public);

    {
        // Lets start with a method which doesn't do anything.
        var parentImplementMeToDoNothing = typeof(ITestInterface).GetMethod(nameof(ITestInterface.ImplementMeToDoNothing));

        // Define the method to implement 'ImplementMeToDoNothing'. It doesn't need to have 
        // the same name, but for simplicity sake, it does.
        var doNothingMethodBuilder = classBuilder.DefineMethod(
            name: nameof(ITestInterface.ImplementMeToDoNothing),
            attributes: MethodAttributes.Public | MethodAttributes.Virtual,
            returnType: typeof(void),
            parameterTypes: Type.EmptyTypes);

        // Get an IL generator and just return.
        var ilGenerator = doNothingMethodBuilder.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ret);

        // Make the method as implementing the definition on the interface.
        classBuilder.DefineMethodOverride(doNothingMethodBuilder, parentImplementMeToDoNothing);
    }

    {
        // Now lets do the method which returns the default int.
        var parentImplementMeToReturnDefaultInt = typeof(ITestInterface).GetMethod(nameof(ITestInterface.ImplementMeToReturnDefaultInt));
        var defaultIntMethodBuilder = classBuilder.DefineMethod(
            name: nameof(ITestInterface.ImplementMeToReturnDefaultInt), 
            attributes: MethodAttributes.Public | MethodAttributes.Virtual, 
            returnType: typeof(int), 
            parameterTypes: Type.EmptyTypes);

        var ilGenerator = defaultIntMethodBuilder.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ldc_I4_0);
        ilGenerator.Emit(OpCodes.Ret);

        classBuilder.DefineMethodOverride(defaultIntMethodBuilder, parentImplementMeToReturnDefaultInt);
    }

    {
        // Implement a method which throws a "NotImplementedException".
        var parentImplementMeToThrowAnException = typeof(ITestInterface).GetMethod(nameof(ITestInterface.ImplementMeToThrowAnException));
        var throwExceptionBuilder = classBuilder.DefineMethod(nameof(ITestInterface.ImplementMeToThrowAnException), MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes);
        var ilGenerator = throwExceptionBuilder.GetILGenerator();

        // Load the string we are going to pass into the constructor of "NotImplementedException" onto the execution stack.
        ilGenerator.Emit(OpCodes.Ldstr, $"The method ImplementMeToThrowAnException is not implemented.");

        // Create a new NotImplementedException(string)
        ilGenerator.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new[] { typeof(string) }));

        // Throw the exception
        ilGenerator.Emit(OpCodes.Throw);
        ilGenerator.Emit(OpCodes.Ret);

        classBuilder.DefineMethodOverride(throwExceptionBuilder, parentImplementMeToThrowAnException);
    }

    {
        // Implement a method which returns the argument.
        var parentImplementMeToReturnMyArgument = typeof(ITestInterface).GetMethod(nameof(ITestInterface.ImplementMeToReturnMyArgument));
        var implementReturnArgument = classBuilder.DefineMethod(nameof(ITestInterface.ImplementMeToReturnMyArgument), MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), new[] { typeof(int) });
        var ilGenerator = implementReturnArgument.GetILGenerator();

        // Since this is a class method, arg0 is always "this". So arg1 is the first argument.
        ilGenerator.Emit(OpCodes.Ldarg_1);
        ilGenerator.Emit(OpCodes.Ret);

        classBuilder.DefineMethodOverride(implementReturnArgument, parentImplementMeToReturnMyArgument);
    }

    // Finish the type.
    var createdType = classBuilder.CreateType();

    // Create a new instance of the type we just created.
    return (ITestInterface)Activator.CreateInstance(createdType);
}

There it is, the implementation provides four different examples. Another implementation strategy is to create the implementations to execute a delegate, so that you can execute whatever you like.

Leave a Reply

Your email address will not be published. Required fields are marked *