Skip to main content

C# Reflection Emit

Introduction

Reflection.Emit is a powerful feature in C# that extends the capabilities of standard reflection by allowing you to dynamically generate code at runtime. While regular reflection lets you inspect existing types and assemblies, Reflection.Emit enables you to create new types, methods, and even entire assemblies on the fly.

This feature lets you write code that writes code—a technique often called metaprogramming. It's like having a miniature C# compiler available to your application while it runs.

In this tutorial, we'll explore:

  • The basics of Reflection.Emit
  • How to create dynamic assemblies and modules
  • Defining custom types at runtime
  • Creating methods with IL (Intermediate Language) code
  • Practical applications of dynamic code generation

Prerequisites

To follow along with this tutorial, you should have:

  • Basic understanding of C# programming
  • Familiarity with basic reflection concepts
  • Some knowledge of object-oriented programming principles

Understanding Reflection.Emit Basics

The Reflection.Emit namespace provides classes for generating MSIL (Microsoft Intermediate Language) code at runtime. MSIL is the intermediate language that C# code compiles to before being further compiled to machine code by the JIT (Just-In-Time) compiler.

The main classes you'll work with include:

  • AssemblyBuilder: Creates dynamic assemblies
  • ModuleBuilder: Creates modules within assemblies
  • TypeBuilder: Defines new types (classes, interfaces, structures)
  • MethodBuilder: Defines methods within types
  • ILGenerator: Emits the actual IL instructions

The process of dynamic code generation typically follows this pattern:

  1. Create a dynamic assembly
  2. Define a module within the assembly
  3. Define a type (class) within the module
  4. Define methods within the type
  5. Generate IL code for the methods
  6. Complete the type and use it

Let's start with a simple example to understand the process.

Creating Your First Dynamic Assembly

Here's how to create a basic dynamic assembly:

csharp
using System;
using System.Reflection;
using System.Reflection.Emit;

public class ReflectionEmitDemo
{
public static void Main()
{
// Create a dynamic assembly name
AssemblyName assemblyName = new AssemblyName("DynamicAssembly");

// Create a dynamic assembly in the current AppDomain
// The AssemblyBuilderAccess.Run parameter means the assembly can be executed
// but not saved to disk
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(
assemblyName,
AssemblyBuilderAccess.Run);

// Create a dynamic module in the assembly
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");

Console.WriteLine($"Created dynamic assembly: {assemblyBuilder.GetName().Name}");
Console.WriteLine($"With module: {moduleBuilder.Name}");
}
}

Output:

Created dynamic assembly: DynamicAssembly
With module: DynamicModule

Defining a Type at Runtime

Now, let's expand our example to define a new type within our dynamic assembly:

csharp
// Continue from previous example...

// Define a public class named "DynamicType" in the module
TypeBuilder typeBuilder = moduleBuilder.DefineType(
"DynamicType",
TypeAttributes.Public | TypeAttributes.Class);

// Complete the type creation
Type dynamicType = typeBuilder.CreateType();

Console.WriteLine($"Created type: {dynamicType.Name}");
Console.WriteLine($"In namespace: {dynamicType.Namespace ?? "(none)"}");
Console.WriteLine($"Is class: {dynamicType.IsClass}");

// Create an instance of our dynamic type
object instance = Activator.CreateInstance(dynamicType);
Console.WriteLine($"Created an instance of type: {instance.GetType()}");

Output:

Created dynamic assembly: DynamicAssembly
With module: DynamicModule
Created type: DynamicType
In namespace: (none)
Is class: True
Created an instance of type: DynamicType

Adding Fields and Properties

Let's make our dynamic type more useful by adding fields and properties:

csharp
// Define a private field
FieldBuilder fieldBuilder = typeBuilder.DefineField(
"_name", // Field name
typeof(string), // Field type
FieldAttributes.Private);

// Define a public Name property
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
"Name", // Property name
PropertyAttributes.None,
typeof(string), // Property type
null); // No parameters, this is not an indexer

// Define the get accessor method for the property
MethodBuilder getMethodBuilder = typeBuilder.DefineMethod(
"get_Name",
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
typeof(string),
Type.EmptyTypes);

// Generate IL for the get accessor
ILGenerator getIL = getMethodBuilder.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0); // Load 'this'
getIL.Emit(OpCodes.Ldfld, fieldBuilder); // Load the private field
getIL.Emit(OpCodes.Ret); // Return the value

// Define the set accessor method for the property
MethodBuilder setMethodBuilder = typeBuilder.DefineMethod(
"set_Name",
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null,
new Type[] { typeof(string) });

// Generate IL for the set accessor
ILGenerator setIL = setMethodBuilder.GetILGenerator();
setIL.Emit(OpCodes.Ldarg_0); // Load 'this'
setIL.Emit(OpCodes.Ldarg_1); // Load the input value
setIL.Emit(OpCodes.Stfld, fieldBuilder); // Store it in the private field
setIL.Emit(OpCodes.Ret); // Return

// Associate the property with its get and set methods
propertyBuilder.SetGetMethod(getMethodBuilder);
propertyBuilder.SetSetMethod(setMethodBuilder);

Now let's use our property:

csharp
// Create the type and instantiate it
Type dynamicType = typeBuilder.CreateType();
object instance = Activator.CreateInstance(dynamicType);

// Use reflection to set and get the property value
PropertyInfo propInfo = dynamicType.GetProperty("Name");
propInfo.SetValue(instance, "John Doe");
string name = (string)propInfo.GetValue(instance);

Console.WriteLine($"Property value: {name}");

Output:

Property value: John Doe

Adding Methods with Parameters and Return Values

Now let's add a method to our dynamic type:

csharp
// Define a method that takes a string parameter and returns a string
MethodBuilder methodBuilder = typeBuilder.DefineMethod(
"Greet", // Method name
MethodAttributes.Public,
typeof(string), // Return type
new Type[] { typeof(string) }); // Parameter types

// Generate the IL for the method
ILGenerator methodIL = methodBuilder.GetILGenerator();

// Local string variable for concatenation
LocalBuilder resultLocal = methodIL.DeclareLocal(typeof(string));

// Load string "Hello, " onto the stack
methodIL.Emit(OpCodes.Ldstr, "Hello, ");
// Load the parameter (greeting parameter) onto the stack
methodIL.Emit(OpCodes.Ldarg_1);
// Call string.Concat to join the strings
methodIL.Emit(OpCodes.Call, typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) }));
// Store the result in our local variable
methodIL.Emit(OpCodes.Stloc, resultLocal);
// Load the result for return
methodIL.Emit(OpCodes.Ldloc, resultLocal);
// Return
methodIL.Emit(OpCodes.Ret);

// Create and use the type
Type dynamicType = typeBuilder.CreateType();
object instance = Activator.CreateInstance(dynamicType);

// Call the dynamically created method
MethodInfo greetMethod = dynamicType.GetMethod("Greet");
string greeting = (string)greetMethod.Invoke(instance, new object[] { "Dynamic World!" });

Console.WriteLine(greeting);

Output:

Hello, Dynamic World!

Real-World Application: Dynamic Proxy Generation

One practical use of Reflection.Emit is creating dynamic proxies. Let's implement a simple logging proxy that wraps an interface and logs method calls:

csharp
using System;
using System.Reflection;
using System.Reflection.Emit;

public class ProxyGenerator
{
public static T CreateLoggingProxy<T>(T target) where T : class
{
if (!typeof(T).IsInterface)
throw new ArgumentException("T must be an interface type");

// Create dynamic assembly and module
AssemblyName assemblyName = new AssemblyName("DynamicProxyAssembly");
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(
assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicProxyModule");

// Create proxy class that implements interface T
string proxyTypeName = typeof(T).Name + "Proxy";
TypeBuilder typeBuilder = moduleBuilder.DefineType(
proxyTypeName,
TypeAttributes.Public | TypeAttributes.Class,
typeof(object),
new Type[] { typeof(T) });

// Add a private field to hold the target instance
FieldBuilder targetField = typeBuilder.DefineField(
"_target", typeof(T), FieldAttributes.Private);

// Create constructor
ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new Type[] { typeof(T) });

ILGenerator ctorIL = ctorBuilder.GetILGenerator();
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); // Call base constructor
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Ldarg_1);
ctorIL.Emit(OpCodes.Stfld, targetField); // Store target in field
ctorIL.Emit(OpCodes.Ret);

// Implement each interface method
foreach (MethodInfo method in typeof(T).GetMethods())
{
// Get parameter types
Type[] paramTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();

// Define the method in our proxy
MethodBuilder methodBuilder = typeBuilder.DefineMethod(
method.Name,
MethodAttributes.Public | MethodAttributes.Virtual,
method.ReturnType,
paramTypes);

// Generate the IL for the method
ILGenerator methodIL = methodBuilder.GetILGenerator();

// Log the method call
methodIL.EmitWriteLine($"Calling method: {method.Name}");

LocalBuilder resultLocal = null;
if (method.ReturnType != typeof(void))
{
resultLocal = methodIL.DeclareLocal(method.ReturnType);
}

// Call the method on the target
methodIL.Emit(OpCodes.Ldarg_0);
methodIL.Emit(OpCodes.Ldfld, targetField); // Load target field

// Load all the method parameters
for (int i = 1; i <= paramTypes.Length; i++)
{
methodIL.Emit(OpCodes.Ldarg, i);
}

// Call the original method on the target
methodIL.Emit(OpCodes.Callvirt, method);

// If there's a return value, store it and load it for return
if (method.ReturnType != typeof(void))
{
methodIL.Emit(OpCodes.Stloc, resultLocal);
methodIL.EmitWriteLine($"Method {method.Name} completed");
methodIL.Emit(OpCodes.Ldloc, resultLocal);
}
else
{
methodIL.EmitWriteLine($"Method {method.Name} completed");
}

methodIL.Emit(OpCodes.Ret);
}

// Create the type
Type proxyType = typeBuilder.CreateType();

// Create an instance of the proxy and return it
return (T)Activator.CreateInstance(proxyType, new object[] { target });
}
}

// Example usage:
public interface ICalculator
{
int Add(int a, int b);
int Subtract(int a, int b);
}

public class Calculator : ICalculator
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
}

public class Program
{
public static void Main()
{
// Create the original calculator
ICalculator calculator = new Calculator();

// Create a logging proxy
ICalculator loggingCalculator = ProxyGenerator.CreateLoggingProxy(calculator);

// Use the proxy
int result = loggingCalculator.Add(5, 3);
Console.WriteLine($"Result: {result}");

result = loggingCalculator.Subtract(10, 4);
Console.WriteLine($"Result: {result}");
}
}

Output:

Calling method: Add
Method Add completed
Result: 8
Calling method: Subtract
Method Subtract completed
Result: 6

Performance Considerations

While Reflection.Emit is incredibly powerful, it comes with some important considerations:

  1. Performance overhead: Generating code at runtime has an upfront cost. It's usually only justified if the generated code will be reused many times.

  2. Debugging challenges: Debugging dynamically generated code can be difficult since it doesn't exist in your source files.

  3. Security implications: Dynamic code generation can potentially introduce security vulnerabilities if not properly managed, especially if the generated code comes from external inputs.

  4. Maintainability: Code that uses Reflection.Emit can be harder to understand and maintain than standard C# code.

When to Use Reflection.Emit

Reflection.Emit is best used in specific scenarios such as:

  • Implementing dynamic proxies (as shown above)
  • Building expression trees for LINQ providers
  • Creating data access layers that generate specialized code for each entity
  • Implementing aspect-oriented programming features
  • Creating compilers or interpreters for domain-specific languages

For most common programming tasks, standard C# features are simpler and more maintainable.

Summary

C# Reflection.Emit provides a powerful way to generate code at runtime. We've covered:

  • Creating dynamic assemblies and modules
  • Defining new types with fields, properties, and methods
  • Generating IL code for method implementations
  • A real-world example of creating a dynamic proxy

Reflection.Emit is an advanced feature that's most useful in frameworks, libraries, and tools rather than in everyday application code. It unlocks possibilities that would otherwise be impossible, letting you create highly adaptable and extensible systems.

Additional Resources

Exercises

  1. Create a dynamic class with a method that adds two integers and returns the result.

  2. Extend the logging proxy example to log method parameters and return values.

  3. Create a dynamic class that implements multiple interfaces.

  4. Build a simple factory that generates different implementations of an interface based on runtime conditions.

  5. Create a dynamic class that includes constructor parameters and uses them to initialize fields.

Happy coding with Reflection.Emit!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)