Matt Hope
Posted on May 31, 2020
I recently decided to undertake some interesting work around building a library .Net Core style host builder for Xamarin apps. As part of that work I encountered a strange hurdle. Essentially, I needed to define a brand new type that extended a base class and implemented an interface...all at runtime. At first, it seemed better to completely avoid this scenario; however, in the goal of complete simplicitly and readability of the end-user code needed for this library, I decided to tackle this challenge head on!
Step 1 - To actually define a type at runtime...
The first task was of course to actually build this mysterious type at runtime. Luckily System.Reflection.Emit has us covered. This assembly allows us to generate in-memory assemblies and take use of the TypeBuilder. To generate a simple type looks something like this:
public Type GenerateType()
{
// Build the dynamic assembly
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly("SuperFancyAssembly", AssemblyBuilderAccess.Run);
// Build the dynamic type
var typeBuilder = assemblyBuilder.DefineDynamicModule("SuperFancyModule")
.DefineType($"SuperFancyType");
return typeBuilder.CreateTypeInfo();
}
Step 2 - Inherit the base class and implement the interface
Of course, generating a type is all fine and dandy but it is not quite as fun if we do not inherit a class or implement an interface. Doing that is actually much simpler than it sounds. Take a look:
public Type GenerateType()
{
// Build the dynamic assembly
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly("SuperFancyAssembly", AssemblyBuilderAccess.Run);
// Build the dynamic type
var typeBuilder = assemblyBuilder.DefineDynamicModule("SuperFancyModule")
.DefineType($"SuperFancyType");
// Add the interface implementation
typeBuilder.AddInterfaceImplementation(typeof(ISuperFancyInterface));
// Inherit the base class
typeBuilder.SetParent(typeof(SuperFancyBaseClass));
return typeBuilder.CreateTypeInfo();
}
Step 3 - Inject IL to generate pass-through constructors (The worst bit)
This is where things get a little bit more complicated.The first thing we need to do is to copy over the constructors from our SuperFancyBaseClass
:
// Get all of the constructors from the SuperFancyBaseClass type
var constructors = typeof(SuperFancyBaseClass).GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
// Loop through each constructor
foreach (var constructor in constructors)
{
// Get all of the parameters from the constructor
var parameters = constructor.GetParameters();
// Get all of the types from parameters
var parameterTypes = parameters.Select(p => p.ParameterType).ToArray();
// Build the new constructor on the new type we are creating
var newConstructor = typeBuilder.DefineConstructor(MethodAttributes.Public, constructor.CallingConvention, parameterTypes);
// Loop through each parameter in the constructor
for (var i = 0; i < parameters.Length; ++i)
{
var parameter = parameters[i];
// Define the parameter
var parameterBuilder = newConstructor.DefineParameter(i + 1, parameter.Attributes, parameter.Name);
}
}
Now we have defined the constructors, we need to generate the IL to call into our base constructors:
// Get the IL generator from the new constructor we defined earlier
var emitter = newConstructor.GetILGenerator();
emitter.Emit(OpCodes.Nop);
// Load `this` and call base constructor with arguments
emitter.Emit(OpCodes.Ldarg_0);
for (var i = 1; i <= parameters.Length; ++i)
{
emitter.Emit(OpCodes.Ldarg, i);
}
emitter.Emit(OpCodes.Call, constructor);
emitter.Emit(OpCodes.Ret);
Once we put it all together it should look something like this:
public Type GenerateType()
{
// Build the dynamic assembly
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly("SuperFancyAssembly", AssemblyBuilderAccess.Run);
// Build the dynamic type
var typeBuilder = assemblyBuilder.DefineDynamicModule("SuperFancyModule")
.DefineType($"SuperFancyType");
// Add the interface implementation
typeBuilder.AddInterfaceImplementation(typeof(ISuperFancyInterface));
// Inherit the base class
typeBuilder.SetParent(typeof(SuperFancyBaseClass));
// Get all of the constructors from the SuperFancyBaseClass type
var constructors = typeof(SuperFancyBaseClass).GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
// Loop through each constructor
foreach (var constructor in constructors)
{
// Get all of the parameters from the constructor
var parameters = constructor.GetParameters();
// Get all of the types from parameters
var parameterTypes = parameters.Select(p => p.ParameterType).ToArray();
// Build the new constructor on the new type we are creating
var newConstructor = typeBuilder.DefineConstructor(MethodAttributes.Public, constructor.CallingConvention, parameterTypes);
// Loop through each parameter in the constructor
for (var i = 0; i < parameters.Length; ++i)
{
var parameter = parameters[i];
// Define the parameter
var parameterBuilder = newConstructor.DefineParameter(i + 1, parameter.Attributes, parameter.Name);
}
// Get the IL generator from the new constructor we defined earlier
var emitter = newConstructor.GetILGenerator();
emitter.Emit(OpCodes.Nop);
// Load `this` and call base constructor with arguments
emitter.Emit(OpCodes.Ldarg_0);
for (var i = 1; i <= parameters.Length; ++i)
{
emitter.Emit(OpCodes.Ldarg, i);
}
emitter.Emit(OpCodes.Call, constructor);
emitter.Emit(OpCodes.Ret);
}
return typeBuilder.CreateTypeInfo();
}
And there you have it! We just defined our own type, implemented an interface, inherited a base class, defined a constructor and called the base constructor all at runtime.
Click here for the source code in action
TLDR: I needed to implement an interface to an existing class at runtime, which required some fun with dynamic assemblies and IL generation
Posted on May 31, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.