using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Reflection.Emit;
public static void Main()
var ctor = GenerateProxy(
var stringBuilder = new StringBuilder();
stringBuilder.Append("Hello");
var proxy = (IProxyMe)ctor.Invoke(new object[]{ stringBuilder });
Console.WriteLine(proxy.String);
private static ConstructorInfo GenerateProxy(Type interfaceType, Expression<Func<StringBuilder,string>> injection)
var proxiedValueType = typeof(StringBuilder);
var assemblyName = new AssemblyName()
Name = $"DynamicAssembly.DynamicImpl.{interfaceType.Name}"
var assembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule(assemblyName.Name);
var type = module.DefineType(
interfaceType.Name + "Impl",
new Type[] { interfaceType });
var searchResultEntryField = type.DefineField($"m_{proxiedValueType.Name}", proxiedValueType, FieldAttributes.Private);
var ctor = type.DefineConstructor(
CallingConventions.Standard,
new Type[] { proxiedValueType });
var param = ctor.DefineParameter(0, ParameterAttributes.None, "result");
var il = ctor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)!);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, searchResultEntryField);
var iPropertyString = typeof(IProxyMe).GetProperty("String");
var propertyDelegate = (Func<StringBuilder,string>)(stringBuilder => stringBuilder.ToString());
AddStaticFieldBackedProperty(
baseProperty: iPropertyString,
getterProxiedFields: new FieldInfo[]{ searchResultEntryField },
getterDelegateType: propertyDelegate.GetType());
var toupe = type.CreateType();
toupe.GetField(GetGetterHookName(iPropertyString), BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, propertyDelegate);
return toupe.GetConstructor(Type.EmptyTypes);
private static string GetGetterHookName(PropertyInfo baseProperty)
return $"{baseProperty.Name}_getDelegate";
private static string GetSetterHookName(PropertyInfo baseProperty)
return $"{baseProperty.Name}_setDelegate";
private static void AddStaticFieldBackedProperty(
PropertyInfo baseProperty,
FieldInfo[]? getterProxiedFields,
Type? getterDelegateType)
var property = typeBuilder.DefineProperty(
$"{baseProperty.ReflectedType!.FullName}.{baseProperty.Name}",
baseProperty.PropertyType,
if (baseProperty.CanRead)
if (getterProxiedFields == null || getterDelegateType == null)
throw new ArgumentException($"{baseProperty.Name}", nameof(baseProperty));
var delegateField = typeBuilder.DefineField(
GetGetterHookName(baseProperty),
FieldAttributes.Private | FieldAttributes.Static);
var getPropertyMethod = typeBuilder.DefineMethod(
$"{baseProperty.ReflectedType!.FullName}.get_{baseProperty.Name}",
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig);
var il = getPropertyMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
foreach (var field in getterProxiedFields)
il.Emit(OpCodes.Ldflda, field);
il.Emit(OpCodes.Ldfld, delegateField);
il.Emit(OpCodes.Callvirt, delegateField.FieldType.GetMethod("Invoke")!);
property.SetGetMethod(getPropertyMethod);
if (baseProperty.CanWrite)
throw new NotSupportedException("Properties with read and write.");
public interface IProxyMe