我有一个方法,创建一个MethodBuilder
方法,并定义使用ILGenerator
和Emit + OpCodes
的行为。
这个方法是在我之前的StackOverflow问题的帮助下创建的-感谢@Marc Gravell的帮助
行为是非常具体的,有一个原因,我必须使用MethodBuilder
而不是c#中的标准方法定义创建一个方法,该方法计划用于在运行时修补逻辑的和谐后缀HarmonyMethod
定义,我不想使用静态方法,因为我将做很多方法模拟,我不想有很多静态类和方法,所以我创建了以下类:
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;
}
}
这个类的用法如下:
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,",")}]");
这工作得很好——除了它缺少一个功能,我希望——这是有产权的PostFixPatchFactory
称为TimesExcecuted
之类的品种,这样我可以跟踪方法多少次我修补excecuted——我努力实现这个特性——我想要这个功能的一部分IL发出新方法中的代码定义,我希望这个方法可以public
实例字段或属性,可以使用如下:
int timesExcecuted = postFixPatchFactory.TimesExcecuted;
Console.WriteLine($"Method has been Triggered: {timesExcecuted}");
请帮助我-我不是很擅长IL或操作码,所以任何帮助都会非常感激
我试过使用一些新的FieldBuilder:
FieldBuilder prop = typeBuilder.DefineField($"DynamicProperty_{Guid.NewGuid()}", typeof(int),
FieldAttributes.Public | FieldAttributes.Static);
并使用以下ILCode中的内容:
il.Emit(OpCodes.Ldsfld, prop);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
但这不起作用,并返回一个无效代码异常
il.Emit(OpCodes.Ldsfld, prop);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
你要离开"prop + 1"在堆栈上。您需要将其保存回
字段il.Emit(OpCodes.Stsfld, prop);
我在。net 6中尝试了以下代码
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);
FieldBuilder prop = typeBuilder.DefineField($"DynamicProperty_{Guid.NewGuid()}", typeof(int),
FieldAttributes.Public | FieldAttributes.Static);
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.Ldsfld, prop);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Stsfld, prop);
il.Emit(OpCodes.Ret);
var t = typeBuilder.CreateType();
var ot = Activator.CreateInstance(t);
var m = t.GetMethod("PostFix");
for (var i = 0; i < 5; i++)
m.Invoke(null, new Object[] { value });
var propVal = t.GetField(prop.Name).GetValue(null);
Debug.WriteLine($"propVal is {propVal}");
输出为
propVal is 5