Skip to content

Instantly share code, notes, and snippets.

@ReubenBond
Created October 12, 2016 01:58
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ReubenBond/1bf2b1bf92ab02dc31242462f7bf7958 to your computer and use it in GitHub Desktop.
Save ReubenBond/1bf2b1bf92ab02dc31242462f7bf7958 to your computer and use it in GitHub Desktop.
Generating static fields on the fly in C# (for use in other codegen)
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Reflection.Emit;
internal class ILFieldBuilder
{
private static readonly AssemblyBuilder AssemblyBuilder =
AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName(nameof(ILFieldBuilder)),
AssemblyBuilderAccess.RunAndCollect);
private static readonly ModuleBuilder ModuleBuilder = AssemblyBuilder.DefineDynamicModule(
nameof(ILFieldBuilder));
private readonly ConcurrentDictionary<object, FieldInfo> staticFields =
new ConcurrentDictionary<object, FieldInfo>(new ReferenceEqualsComparer());
/// <summary>
/// Gets or creates a <see langword="static"/>, <see langword="readonly"/> field which holds the specified
/// <paramref name="value"/>.
/// </summary>
/// <typeparam name="T">The underlying type of the provided value.</typeparam>
/// <param name="value">The value.</param>
/// <returns>The field which holds the provided <paramref name="value"/>.</returns>
public FieldInfo GetOrCreateStaticField<T>(T value)
{
return this.staticFields.GetOrAdd(value, CreateField);
}
/// <summary>
/// Creates a static field in a new class and initializes it with the provided <paramref name="value"/>.
/// </summary>
/// <typeparam name="T">The type of the field.</typeparam>
/// <param name="value">The value to initialize the field with.</param>
/// <returns>The newly created static field.</returns>
private static FieldInfo CreateField<T>(T value)
{
// Create a new type to hold the field.
var typeBuilder = ModuleBuilder.DefineType(
typeof(T).Name + Guid.NewGuid().ToString("N"),
TypeAttributes.NotPublic | TypeAttributes.Class | TypeAttributes.Sealed);
// Create a static field to hold the value.
const string FieldName = "Instance";
var field = typeBuilder.DefineField(
FieldName,
typeof(T),
FieldAttributes.Static | FieldAttributes.InitOnly | FieldAttributes.Public);
// Create a method to initialize the field.
const string MethodName = "Initialize";
var initMethod = typeBuilder.DefineMethod(
MethodName,
MethodAttributes.Static | MethodAttributes.Private,
CallingConventions.Standard,
typeof(void),
new[] { typeof(T) });
var il = initMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stsfld, field);
il.Emit(OpCodes.Ret);
// Build the type.
var declaringType = typeBuilder.CreateType();
// Invoke the initializer method using reflection, passing the provided value to initialize the new field.
declaringType.GetMethod(MethodName, BindingFlags.Static | BindingFlags.NonPublic)
.Invoke(null, new object[] { value });
return declaringType.GetField(FieldName);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment