I have a Method that creates a MethodBuilder Method and defines the Behaviour using ILGenerator and Emit + OpCodes.
This Method was created with the help of a previous StackOverflow Question I made - Thanks to @Marc Gravell for the help
The Behaviour is very specific and there is a reason I must create a method using MethodBuilder rather than standard method definition in C#, The Method is planned to be used for a Harmony PostFix HarmonyMethod Definition to Patch the Logic at runtime, and I don't want to use a static Method since ill be doing a lot of Method Mocks and I don't want to have a lot of static classes and methods, So I Created the following Class:
public class PostFixPatchFactory<T> { public T Value { get; set; } public Exception Exception { get; set; } public PostFixPatchFactory(T value) => Value = value; public int TimesTriggered; public FieldInfo field; public MethodInfo GeneratePostfix() { string fieldName = $"DynamicField_{Guid.NewGuid()}"; AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName($"DynamicAssembly_{Guid.NewGuid()}"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule($"DynamicModule_{Guid.NewGuid()}"); TypeBuilder typeBuilder = module.DefineType($"DynamicType_{Guid.NewGuid()}", TypeAttributes.Public); FieldBuilder field = typeBuilder.DefineField(fieldName, typeof(T), FieldAttributes.Private | FieldAttributes.Static); if (Value is Exception) { typeBuilder.DefineMethod("PostFix", MethodAttributes.Static | MethodAttributes.Public, typeof(void), null) .GetILGenerator().ThrowException(typeof(T)); } else { MethodBuilder method = typeBuilder.DefineMethod("PostFix", MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { typeof(T).MakeByRefType() }); var il = method.GetILGenerator(); method.DefineParameter(1, ParameterAttributes.None, "__result"); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldsfld, field); il.Emit(OpCodes.Stobj, typeof(T)); il.Emit(OpCodes.Ret); } var type = typeBuilder.CreateType(); type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, Value); return type.GetMethod("PostFix", BindingFlags.Public | BindingFlags.Static); } public bool Prefix() { TimesTriggered++; return false; } } This Class is used as follows:
var harmony = new Harmony(nameof(Program)); HarmonyMethod prefix = new HarmonyMethod(typeof(Program).GetMethod(nameof(Prefix))); var postFixPatchFactory = new PostFixPatchFactory<long>(6); MethodInfo originalMethod = typeof(Program).GetMethod(nameof(TestMethodToChange)); MethodInfo scenarioToMockExample2 = typeof(ScenariosToMock).GetMethod("Example", BindingFlags.Static | BindingFlags.NonPublic); HarmonyMethod scenarioToMockExample2PostFix = new HarmonyMethod(g.GeneratePostfix()); harmony.Patch(scenarioToMockExample2, prefix, scenarioToMockExample2PostFix); var result = TestMethodToChange(); Console.WriteLine($"list result:[{result.Join(null,",")}]"); This works very nicely - except its missing one feature that I want - which is to have a property on the PostFixPatchFactory called TimesExcecuted or something of that variety so that I can track how many times a Method I patched got excecuted - I am struggling to Implement this feature - I want this feature to be part of the IL Emit Code within the new method definition - and I want the method to be accessible as an Instance public field or Property so that it can be used as follows:
int timesExcecuted = postFixPatchFactory.TimesExcecuted; Console.WriteLine($"Method has been Triggered: {timesExcecuted}"); Please Help me - i'm not very good at IL or Opcodes so any help would be greatly Appreciated
I have tried using something along the lines of a new FieldBuilder:
FieldBuilder prop = typeBuilder.DefineField($"DynamicProperty_{Guid.NewGuid()}", typeof(int), FieldAttributes.Public | FieldAttributes.Static); And use something along the lines of the following ILCode:
il.Emit(OpCodes.Ldsfld, prop); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Add); But this did not work and returned a Invalid Code Exception