Dynamic Runtime Class Generation in C# Using System.Reflection.Emit

Nov 21, 2025 · Programming · 10 views · 7.8

Keywords: C# | .NET | Reflection.Emit | Dynamic Class | Runtime Generation | CodeDom | DynamicObject

Abstract: This article explores methods for dynamically creating classes at runtime in C#, focusing on System.Reflection.Emit. It provides step-by-step examples, explains the implementation, and compares alternative approaches like CodeDom and DynamicObject for dynamic type generation in .NET applications.

Introduction

In modern software development, there are scenarios where classes need to be generated dynamically at runtime, such as when dealing with dynamic data structures or plugin systems. This article addresses a common use case in C# where a list of field definitions is used to create a class on the fly, without generating physical files.

Overview of Dynamic Class Creation Methods

Several approaches exist for dynamic class generation in .NET, including System.Reflection.Emit, CodeDom, and the dynamic keyword with DynamicObject. Each has its advantages and trade-offs in terms of performance, complexity, and flexibility.

Implementing Dynamic Classes with System.Reflection.Emit

The System.Reflection.Emit namespace provides low-level control over type generation, allowing for efficient runtime class creation. Below is a step-by-step implementation based on the provided scenario.

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

public static class DynamicClassGenerator
{
    public static Type CreateDynamicType(List<Field> fields)
    {
        // Define assembly and module
        AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
        AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");

        // Define type
        TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicClass", TypeAttributes.Public | TypeAttributes.Class);

        // Add default constructor
        ConstructorBuilder constructor = typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);

        // Add properties based on fields
        foreach (var field in fields)
        {
            Type propertyType = GetTypeFromString(field.FieldType);
            CreateProperty(typeBuilder, field.FieldName, propertyType);
        }

        // Create the type and return it
        return typeBuilder.CreateType();
    }

    private static Type GetTypeFromString(string typeName)
    {
        switch (typeName.ToLower())
        {
            case "int": return typeof(int);
            case "string": return typeof(string);
            // Add more types as needed
            default: return Type.GetType(typeName) ?? throw new ArgumentException($"Unknown type: {typeName}");
        }
    }

    private static void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
    {
        // Define private field
        FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

        // Define property
        PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);

        // Define get method
        MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
        ILGenerator getIl = getMethodBuilder.GetILGenerator();
        getIl.Emit(OpCodes.Ldarg_0); // Load this
        getIl.Emit(OpCodes.Ldfld, fieldBuilder); // Load field value
        getIl.Emit(OpCodes.Ret); // Return

        // Define set method
        MethodBuilder setMethodBuilder = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new Type[] { propertyType });
        ILGenerator setIl = setMethodBuilder.GetILGenerator();
        setIl.Emit(OpCodes.Ldarg_0); // Load this
        setIl.Emit(OpCodes.Ldarg_1); // Load value
        setIl.Emit(OpCodes.Stfld, fieldBuilder); // Store to field
        setIl.Emit(OpCodes.Ret); // Return

        // Associate methods with property
        propertyBuilder.SetGetMethod(getMethodBuilder);
        propertyBuilder.SetSetMethod(setMethodBuilder);
    }
}

public class Field
{
    public string FieldName { get; set; }
    public string FieldType { get; set; }
}

// Example usage
// List<Field> fields = new List<Field>
// {
//     new Field { FieldName = "EmployeeID", FieldType = "int" },
//     new Field { FieldName = "EmployeeName", FieldType = "string" },
//     new Field { FieldName = "Designation", FieldType = "string" }
// };
// Type dynamicType = DynamicClassGenerator.CreateDynamicType(fields);
// object instance = Activator.CreateInstance(dynamicType);

This code defines a dynamic class with properties corresponding to the field definitions. The GetTypeFromString method handles mapping string type names to actual Type objects.

Code Explanation

The implementation involves several key steps: defining a dynamic assembly and module, creating a type builder, adding a default constructor, and generating properties with getter and setter methods using IL generation. Each property is backed by a private field, and the IL code ensures proper field access.

Alternative Approaches

Other methods include using CodeDom for source code generation and compilation, or leveraging C's dynamic features with DynamicObject for simpler cases. CodeDom allows writing C# code as strings and compiling it, while DynamicObject provides a way to handle dynamic members without emitting IL.

Comparison and Best Practices

Reflection.Emit offers high performance and low-level control but requires expertise in IL. CodeDom is easier for developers familiar with C# syntax but may have overhead. DynamicObject is suitable for dynamic data binding but lacks strong typing. Choose based on your application's needs.

Conclusion

Dynamic class generation in C# is a powerful technique enabled by frameworks like System.Reflection.Emit. By understanding and implementing these methods, developers can build flexible and adaptive software systems.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.