A C# framework for generating executable or exportable dynamic types and methods based on Emit API. This framework provides API at a high-abstraction level to improve the development efficiency and correctness.
$ dotnet add package EmitToolboxUsing System.Reflection.Emit to dynamically create types and methods at runtime is
prone to errors and can be quite complex.
To effectively emit dynamic IL code,
one must have a deep understanding of the Common Intermediate Language (CIL) and the .NET runtime.
The EmitToolbox library aims to simplify this process by providing a set of high-level abstractions and utilities.
Contexts:
AssemblyBuildingContext - Context for building dynamic assemblies.TypeBuildingContext - Context for building dynamic types.MethodBuildingContext - Context for building dynamic methods.ActionMethodBuildingContext - Context for building dynamic methods that does not have a return value.FuncMethodBuildingContext - Context for building dynamic methods that has a return value.FieldBuildingContext - Context for building fields of the dynamic types.PropertyBuildingContext - Context for building properties of the dynamic types.using EmitToolbox;
// Define an context for an executable (and cannot be saved) assembly.
var assemblyContext = AssemblyBuildingContext
.CreateExecutableContextBuilder("SampleDynamicAssembly")
.Build();
// Define a context for a class type within the assembly.
var typeContext = assemblyContext.DefineClass("SampleClass");
// Define a field.
var fieldContext = typeContext.Fields.Instance<int>("_value", FieldAttributes.Private);
// Define a method.
var methodContext = typeContext.Functors.Instance("AddAndSet",
[ParameterDefinition.Value<int>()], ResultDefinition.Value<int>());
var argumentSymbol = methodContext.Argument<int>(1)
var fieldSymbol = fieldContext.Symbol(methodContext.This);
var resultSymbol = fieldSymbol.Add(argumentSymbol);
fieldSymbol.Assign(resultSymbol);
methodContext.Return(resultSymbol);
// Generate the type.
typeContext.Build();
// Create an instance of the generated type.
var instance = Activator.CreateInstance(typeContext.BuildingType);
var result1 = methodContext.BuildingMethod.Invoke(null, [1])
Console.WriteLine(result1); // Output: 1
var result2 = methodContext.BuildingMethod.Invoke(null, [2])
Console.WriteLine(result2); // Output: 3
Create a symbol for an argument: methodContext.Argument<int>(0).
Create a local variable: methodContext.Variable<int>().
Create a symbol for a literal value: methodContext.Value(123).
Supported literal types:
byte, sbyte, short, ushort, int, uint, long, ulong;float, double, decimal;bool, char, string;null for reference types.Type, FieldInfo, PropertyInfo, ConstructorInfo, MethodInfo;// Use .AsArray() to convert a symbol of an array type to an array facade.
var array = methodContext.Argument<int[]>(0).AsArray();
var elementFromLiteralIndex = array[0]; // Get an element using a literal index.
var elementFromSymbolIndex = array[methodContext.Argument<int>(1)]; // Get an element using a index symbol.Following code can generate a method which is equal to (bool condition) => condition ? 1 : 0:
var methodContext = typeContext.Functors.Static("Test",
[ParameterDefinition.Value<bool>()], ResultDefinition.Value<int>());
var argument = methodContext.Argument<bool>(0);
methodContext.If(argument,
() =>
{
methodContext.Return(methodContext.Value(1));
},
() =>
{
methodContext.Return(methodContext.Value(0));
});
methodContext.Return(methodContext.Value(-1));For C# code like below:
int method(int arg)
{
while (arg != 0)
{
arg -= 1;
}
return arg;
}Corresponding code to generate such method is:
var methodContext = typeContext.Functors.Static("Test",
[ParameterDefinition.Value<int>()], ResultDefinition.Value<int>());
var argument = methodContext.Argument<int>(0);
methodContext.While(
methodContext.Expression(() =>
argument.IsEqualTo(methodContext.Value(0)).Negate()),
() => argument.SelfSubtract(1));
methodContext.Return(argument);