Codementor Events

Defining types at runtime? why not...

Published Feb 09, 2021

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

Discover and read more posts from Matt Hope
get started
post commentsBe the first to share your opinion
Show more replies