Created
October 12, 2016 01:58
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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