.NET - Dynamically generating classes in runtime

Reflection API in .NET is one of the most powerful and incredible features of it. And along with DLR it enables a whole new look at what can be done in .NET languages.

One of problems i had to solve recently was how to create a unified DSL for queries that could be used with most C# DB drivers(they had to have IQueryable support).

It turned out to be simpler job than i thought. This github project helped me a lot to figure out how to solve it. One of the problems was how to execute projections from dbs and be able to get the information in runtime.

Overview

Solution consists mainly from 4 parts:

An abstract class that serves as a parent for further generation

A class that serves as an information about properties that will represent data fields

A class that’s used as a signature for specified assemblies, since we’re caching them(it will be explained why later)

A singleton factory class that generates, caches and returns dynamic assemblies

Abstract class

It’s just a regular empty abstract class that serves as a base for further assemblies.

public abstract class AnonymousClass { }

Class for information about data fields

public class AnonymousProperty { private string name ; private Type type ; public AnonymousProperty ( string name , Type type ) { if ( String . IsNullOrWhiteSpace ( name )) throw new ArgumentNullException ( nameof ( name )); if ( type == default ( Type )) throw new ArgumentNullException ( nameof ( type )); this . name = name ; this . type = type ; } public string Name => this . name ; public Type Type => this . type ; }

Signature class

A signature class is basically used as a unique hash of dynamically generated assemblies. It takes property names and property types from a generated assembly and XORs their hash codes with it’s own hash code.

public class AnonymousClassSignature : IEquatable < AnonymousClassSignature > { private AnonymousProperty [] properties ; private int hashCode = 0 ; public AnonymousClassSignature ( IEnumerable < AnonymousProperty > properties ) { if ( properties == null ) throw new ArgumentNullException ( nameof ( properties )); this . properties = properties . ToArray (); foreach ( var property in this . properties ) { this . hashCode = this . hashCode ^ property . Name . GetHashCode () ^ property . Type . GetHashCode (); } } public override int GetHashCode () => this . hashCode ; public override bool Equals ( object obj ) => obj is AnonymousClassSignature ? this . Equals ( obj as AnonymousClassSignature ) : false ; public bool Equals ( AnonymousClassSignature other ) { if ( other == null ) throw new ArgumentNullException ( nameof ( other )); if ( this . properties . Length != other . properties . Length ) return false ; for ( var i = 0 ; i < this . properties . Length ; i ++) { if ( this . properties [ i ]. Name != other . properties [ i ]. Name || this . properties [ i ]. Type != other . properties [ i ]. Type ) return false ; } return true ; } }

Singleton factory class

This is a long one. It’s a lazy initialized singleton class that serves as a factory generator of dynamicly created classes and it also holds a cache of already created ones. Caching is needed because dynamically generating a class has a huge performance hit. It uses both a ConcurrentDictionary and ReaderWriterLockSlim to synchronize access to generation and retrieval of dynamic classes.

Steps to get a dynamic class basically goes like this:

Create a list of properties you want a dynamic class to have, with a name and type

Give it to the factory

Factory creates a unique signature out of those properties and checks if it already exists in cache

If it exists retrieve it from cache

If not, use TypeBuilder to create those properties, with backing fields and get_ , set_ methods for each one, cache it and return to the caller

ReflectionPermission is because we need to generate private fields and that is not allowed by default, and basically any action that uses Reflection.Emit requires it.

TypeBuilder is basically just a wrapper around CIL and currently it’s the only way to have dynamically generated classes.