Last active
July 13, 2025 18:33
-
-
Save gotmachine/d6121bd96de68b6bcd52ae2abef203b9 to your computer and use it in GitHub Desktop.
Small helper class for IL-emitting anonymous field getter/setter delegates
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
public struct MyStruct | |
{ | |
private static int staticField; | |
private int field; | |
} | |
public class MyClass | |
{ | |
private static int staticField; | |
private int field; | |
} | |
FieldInfo myStructStaticField = typeof(MyStruct).GetField("staticField", BindingFlags.Static | BindingFlags.NonPublic); | |
FieldAccess.StaticSetter<int> MyStructStaticFieldSetter = FieldAccess.EmitStaticSetter<MyStruct, int>(myStructStaticField); | |
FieldAccess.StaticGetter<int> MyStructStaticFieldGetter = FieldAccess.EmitStaticGetter<MyStruct, int>(myStructStaticField); | |
MyStructStaticFieldSetter(42); | |
int structStaticValue = MyStructStaticFieldGetter(); | |
FieldInfo myStructField = typeof(MyStruct).GetField("field", BindingFlags.Instance | BindingFlags.NonPublic); | |
FieldAccess.StructSetter<MyStruct, int> MyStructFieldSetter = FieldAccess.EmitStructSetter<MyStruct, int>(myStructField); | |
FieldAccess.InstanceGetter<MyStruct, int> MyStructFieldGetter = FieldAccess.EmitInstanceGetter<MyStruct, int>(myStructField); | |
MyStruct myStruct = new MyStruct(); | |
MyStructFieldSetter(ref myStruct, 42); | |
int structValue = MyStructFieldGetter(myStruct); | |
FieldInfo myClassStaticField = typeof(MyClass).GetField("staticField", BindingFlags.Static | BindingFlags.NonPublic); | |
FieldAccess.StaticSetter<int> MyClassStaticFieldSetter = FieldAccess.EmitStaticSetter<MyClass, int>(myClassStaticField); | |
FieldAccess.StaticGetter<int> MyClassStaticFieldGetter = FieldAccess.EmitStaticGetter<MyClass, int>(myClassStaticField); | |
MyClassStaticFieldSetter(42); | |
int classStaticValue = MyClassStaticFieldGetter(); | |
FieldInfo myClassField = typeof(MyClass).GetField("field", BindingFlags.Instance | BindingFlags.NonPublic); | |
FieldAccess.ClassSetter<MyClass, int> MyClassFieldSetter = FieldAccess.EmitClassSetter<MyClass, int>(myClassField); | |
FieldAccess.InstanceGetter<MyClass, int> MyClassFieldGetter = FieldAccess.EmitInstanceGetter<MyClass, int>(myClassField); | |
MyClass myClass = new MyClass(); | |
MyClassFieldSetter(myClass, 42); | |
int classValue = MyClassFieldGetter(myClass); |
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.Reflection; | |
using System.Reflection.Emit; | |
namespace ReflectionUtils | |
{ | |
/// <summary> | |
/// A static helper for IL-emitting anonymous field getters and setters. <para/> | |
/// Provide convenient, type-safe and much faster access to non-public fields than using the <see cref="FieldInfo.GetValue(object)"/> or <see cref="FieldInfo.SetValue(object, object)"/> methods.<para/> | |
/// </summary> | |
public static class FieldAccess | |
{ | |
/// <summary> | |
/// A getter for a static field. | |
/// </summary> | |
/// <typeparam name="TField">The type of the field</typeparam> | |
/// <returns>The value of the field.</returns> | |
public delegate TField StaticGetter<out TField>(); | |
/// <summary> | |
/// Emit a delegate for retrieving the value of the static <paramref name="field"/> of type <typeparamref name="TField"/> on the <typeparamref name="TTarget"/> type. | |
/// </summary> | |
public static StaticGetter<TField> EmitStaticGetter<TTarget, TField>(FieldInfo field) | |
{ | |
return (StaticGetter<TField>)EmitGetter(field, typeof(TTarget), typeof(TField), typeof(StaticGetter<TField>), true); | |
} | |
/// <summary> | |
/// A getter for an instance field. | |
/// </summary> | |
/// <typeparam name="TTarget">The type declaring the field</typeparam> | |
/// <typeparam name="TField">The type of the field</typeparam> | |
/// <param name="target">The <typeparamref name="TTarget"/> instance to retrieve the field from.</param> | |
/// <returns>The value of the field.</returns> | |
public delegate TField InstanceGetter<in TTarget, out TField>(TTarget target); | |
/// <summary> | |
/// Emit a delegate for retrieving the value of <paramref name="field"/> of type <typeparamref name="TField"/> on an instance of type <typeparamref name="TTarget"/>. | |
/// </summary> | |
public static InstanceGetter<TTarget, TField> EmitInstanceGetter<TTarget, TField>(FieldInfo field) | |
{ | |
return (InstanceGetter<TTarget, TField>)EmitGetter(field, typeof(TTarget), typeof(TField), typeof(InstanceGetter<TTarget, TField>), false); | |
} | |
private static Delegate EmitGetter(FieldInfo field, Type declaringType, Type fieldType, Type delegateType, bool isStatic) | |
{ | |
if (!field.FieldType.IsAssignableFrom(fieldType)) | |
throw new ArgumentException($"Type {fieldType} cannot be assigned to type {field.FieldType}"); | |
if (!field.DeclaringType.IsAssignableFrom(declaringType)) | |
throw new ArgumentException($"Type {declaringType} cannot be assigned to type {field.DeclaringType}"); | |
if (isStatic != field.IsStatic) | |
throw new ArgumentException($"Field {field.Name} {(field.IsStatic ? "is" : "isn't")} static"); | |
string methodName = declaringType.FullName + ".get_" + field.Name; | |
DynamicMethod getterMethod = new DynamicMethod(methodName, fieldType, isStatic ? null : new Type[] { declaringType }, true); | |
ILGenerator gen = getterMethod.GetILGenerator(); | |
if (isStatic) | |
{ | |
gen.Emit(OpCodes.Ldsfld, field); | |
} | |
else | |
{ | |
gen.Emit(OpCodes.Ldarg_0); | |
gen.Emit(OpCodes.Ldfld, field); | |
} | |
gen.Emit(OpCodes.Ret); | |
return getterMethod.CreateDelegate(delegateType); | |
} | |
/// <summary> | |
/// A setter for a static field. | |
/// </summary> | |
/// <typeparam name="TField">The type of the field</typeparam> | |
/// <param name="value">The value to assign to the field.</param> | |
public delegate void StaticSetter<in TField>(TField value); | |
/// <summary> | |
/// Emit a delegate for setting the value of the static <paramref name="field"/> of type <typeparamref name="TField"/> on the type <typeparamref name="TTarget"/>. | |
/// </summary> | |
public static StaticSetter<TField> EmitStaticSetter<TTarget, TField>(FieldInfo field) | |
{ | |
return (StaticSetter<TField>)EmitSetter(field, typeof(TTarget), typeof(TField), typeof(StaticSetter<TField>), true); | |
} | |
/// <summary> | |
/// A setter for a class instance field. | |
/// </summary> | |
/// <typeparam name="TTarget">The class declaring the field</typeparam> | |
/// <typeparam name="TField">The type of the field</typeparam> | |
/// <param name="target">The <typeparamref name="TTarget"/> instance to assign the field on.</param> | |
/// <param name="value">The value to assign to the field.</param> | |
public delegate void ClassSetter<in TTarget, in TField>(TTarget target, TField value) where TTarget : class; | |
/// <summary> | |
/// Emit a delegate for setting the value of the <paramref name="field"/> of type <typeparamref name="TField"/> on the instance of a class of type <typeparamref name="TTarget"/>. | |
/// </summary> | |
public static ClassSetter<TTarget, TField> EmitClassSetter<TTarget, TField>(FieldInfo field) where TTarget : class | |
{ | |
return (ClassSetter<TTarget, TField>)EmitSetter(field, typeof(TTarget), typeof(TField), typeof(ClassSetter<TTarget, TField>), false); | |
} | |
/// <summary> | |
/// A setter for a struct instance field. | |
/// </summary> | |
/// <typeparam name="TTarget">The struct declaring the field</typeparam> | |
/// <typeparam name="TField">The type of the field</typeparam> | |
/// <param name="target">The <typeparamref name="TTarget"/> instance to assign the field on.</param> | |
/// <param name="value">The value to assign to the field.</param> | |
public delegate void StructSetter<TTarget, in TField>(ref TTarget target, TField value) where TTarget : struct; | |
/// <summary> | |
/// Emit a delegate for setting the value of the <paramref name="field"/> of type <typeparamref name="TField"/> on the instance of a struct of type <typeparamref name="TTarget"/>. | |
/// </summary> | |
public static StructSetter<TTarget, TField> EmitStructSetter<TTarget, TField>(FieldInfo field) where TTarget : struct | |
{ | |
return (StructSetter<TTarget, TField>)EmitSetter(field, typeof(TTarget), typeof(TField), typeof(StructSetter<TTarget, TField>), false); | |
} | |
private static Delegate EmitSetter(FieldInfo field, Type declaringType, Type fieldType, Type delegateType, bool isStatic) | |
{ | |
if (!field.FieldType.IsAssignableFrom(fieldType)) | |
throw new ArgumentException($"Type {fieldType} cannot be assigned to type {field.FieldType}"); | |
if (!field.DeclaringType.IsAssignableFrom(declaringType)) | |
throw new ArgumentException($"Type {declaringType} cannot be assigned to type {field.DeclaringType}"); | |
if (isStatic != field.IsStatic) | |
throw new ArgumentException($"Field {field.Name} {(field.IsStatic ? "is" : "isn't")} static"); | |
string methodName = declaringType.FullName + ".set_" + field.Name; | |
Type[] paramTypes; | |
if (isStatic) | |
paramTypes = new Type[] { fieldType }; | |
else | |
paramTypes = new Type[] { declaringType.IsValueType ? declaringType.MakeByRefType() : declaringType, fieldType }; | |
DynamicMethod setterMethod = new DynamicMethod(methodName, null, paramTypes, true); | |
ILGenerator gen = setterMethod.GetILGenerator(); | |
if (field.IsStatic) | |
{ | |
gen.Emit(OpCodes.Ldarg_0); | |
gen.Emit(OpCodes.Stsfld, field); | |
} | |
else | |
{ | |
gen.Emit(OpCodes.Ldarg_0); | |
gen.Emit(OpCodes.Ldarg_1); | |
gen.Emit(OpCodes.Stfld, field); | |
} | |
gen.Emit(OpCodes.Ret); | |
return setterMethod.CreateDelegate(delegateType); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment