Create Types on Demand and Cecilifier
This post is part of C# Advent Calendar 2025. Visit it for all the awesome upcoming posts!
The traditional C# Advent is here. Usually I give some daily and practical advice. Like testing tips [1] or some outsider view approach to C# [2] here. This time its more about fun with very niche applications.
Provide 'Just In Time' Types & Code?
In dotNet there are facilities to create types at Runtime. This exists of two things:
A callback that is called when a type isn’t found.
Loading and generating Assemblies at runtime.
The AppDomain.CurrentDomain.TypeResolve callback that you can register. It is called every time a type is not found:
AppDomain.CurrentDomain.TypeResolve += (sender, args) =>
{
Console.Out.WriteLine($"Oh boy, oh boy: The type: {args.Name} is missing");
// But I couldn't find it as well
return null;
};
var myType = Type.GetType("Gamlor.Shinanigans.HelloWorld");
var myInstance = myType.GetConstructors()[0].Invoke([]);
Console.Out.WriteLine($"Say hi from my type: {myInstance}");This will crash, as we didn’t provide the assembly where the type could live,
and the type lookup will return null
Oh boy, oh boy: The type: Gamlor.Shinanigans.HelloWorld is missing
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
at Program.<Main>$(String[] args) in /home/roman/dev/temp/RiderProjects/ConsoleApp1/ConsoleApp1/Program.cs:line 18Then next step is provide some code. We could load an assembly from disk with the missing type. Or we can generate code in a dynamic assembly:
// The AssemblyBuilder allows you to dynamically generate an Assembly
var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("MyDyanicAssembly"), AssemblyBuilderAccess.Run);
var module = ab.DefineDynamicModule("MainModule");
// In the Callback, we generate the missing type
AppDomain.CurrentDomain.TypeResolve += (sender, args) =>
{
var typeDef = module.DefineType(args.Name, TypeAttributes.Public | TypeAttributes.Class);
var toStringMethod = typeDef.DefineMethod("ToString",
MethodAttributes.Private | MethodAttributes.HideBySig |
MethodAttributes.NewSlot | MethodAttributes.Virtual,
typeof(string),
Type.EmptyTypes);
// Generate the code
ILGenerator il = toStringMethod.GetILGenerator();
il.Emit(OpCodes.Ldstr, "Hello World from: " + args.Name);
il.Emit(OpCodes.Ret);
// Override the .ToString method and create the type
typeDef.DefineMethodOverride(toStringMethod, typeof(object).GetMethod("ToString"));
typeDef.CreateType();
// Return our AssemblyBuilder: It is a sub-type of Assembly
return ab;
};
{
// As for a type, and it magically is generated
var myType = Type.GetType("Gamlor.Shinanigans.MyFirstType");
var myInstance = myType.GetConstructors()[0].Invoke([]);
Console.Out.WriteLine($"ToString: {myInstance}");
}
{
// And one more
var myType = Type.GetType("Gamlor.Shinanigans.More.Types");
var myInstance = myType.GetConstructors()[0].Invoke([]);
Console.Out.WriteLine($"ToString: {myInstance}");
}When running, the new types are created and have the ToString method overriden:
ToString: Hello World from: Gamlor.Shinanigans.MyFirstType
ToString: Hello World from: Gamlor.Shinanigans.More.TypesBuilding the IL: cecilifier.me
The hard part for me is always the IL. I don’t know by hard and I need to use it very rarely.
Luckily, there is https://cecilifier.me! Thanks to Adriano.
On this website you can write some C# on the left. On the right it will generate code for the Mono.Cecil library. If you use Mono.Cecil, you can use it directly. However, even without Cecil, you can squint a bit and translate it the built-in libraries.
Update Adriano (the creator of cecilifier.me) told me that if you only need the IL and not the Mono.Cecil specifics, then https://sharplab.io/ and https://lab.razor.fyi/ are great alternatives.
For my ToString example:
I selected an example
ToStringmethod.Then I saw what binding flags and what IL instructions I needed.
Use-cases and Summary
What are the actual use cases for these techniques? It is mostly some kind of frame-work code that will use these capabilities. Like a framework that adds instrumentation/interception at runtime. Another use case is some kind of interpreter / compiler of a DSL.
Summary: You can load and generate IL code just in time in dotNet. And https://cecilifier.me helps you to write code that writes IL.


