gpt4 book ai didi

c# - 动态替换 C# 方法的内容?

转载 作者:IT王子 更新时间:2023-10-29 03:39:32 25 4
gpt4 key购买 nike

我想做的是改变 C# 方法在被调用时的执行方式,这样我就可以这样写:

[Distributed]
public DTask<bool> Solve(int n, DEvent<bool> callback)
{
for (int m = 2; m < n - 1; m += 1)
if (m % n == 0)
return false;
return true;
}

在运行时,我需要能够分析具有分布式属性的方法(我已经可以做到),然后在函数主体执行之前和函数返回之后插入代码。更重要的是,我需要能够在不修改调用 Solve 的代码或在函数开始时(在编译时;在运行时这样做是目标)的情况下执行此操作。

目前我尝试了这段代码(假设t是Solve存储的类型,m是Solve的MethodInfo):

private void WrapMethod(Type t, MethodInfo m)
{
// Generate ILasm for delegate.
byte[] il = typeof(Dpm).GetMethod("ReplacedSolve").GetMethodBody().GetILAsByteArray();

// Pin the bytes in the garbage collection.
GCHandle h = GCHandle.Alloc((object)il, GCHandleType.Pinned);
IntPtr addr = h.AddrOfPinnedObject();
int size = il.Length;

// Swap the method.
MethodRental.SwapMethodBody(t, m.MetadataToken, addr, size, MethodRental.JitImmediate);
}

public DTask<bool> ReplacedSolve(int n, DEvent<bool> callback)
{
Console.WriteLine("This was executed instead!");
return true;
}

但是,MethodRental.SwapMethodBody 仅适用于动态模块;不是那些已经编译并存储在程序集中的。

所以我正在寻找一种方法来有效地对已存储在加载和执行程序集中的方法执行 SwapMethodBody。

请注意,如果我必须将方法完全复制到动态模块中,这不是问题,但在这种情况下,我需要找到一种方法来跨 IL 复制并更新对 Solve() 的所有调用这样他们就会指向新副本。

最佳答案

Disclosure: Harmony is a library that was written and is maintained by me, the author of this post.

Harmony 2是一个开源库(MIT 许可),旨在在运行时替换、修饰或修改任何类型的现有 C# 方法。它主要关注用 Mono 或 .NET 编写的游戏和插件。它负责对同一方法进行多次更改 - 它们会累积而不是相互覆盖。

它为每个原始方法创建动态替换方法,并向它们发出代码,在开始和结束时调用自定义方法。它还允许您编写过滤器来处理原始 IL 代码和自定义异常处理程序,从而允许对原始方法进行更详细的操作。

为了完成这个过程,它将一个简单的汇编器跳转到原始方法的蹦床中,指向从编译动态方法生成的汇编器。这适用于 Windows、macOS 和 Mono 支持的任何 Linux 上的 32/64 位。

可以找到文档here .

例子

( Source )

原始代码

public class SomeGameClass
{
private bool isRunning;
private int counter;

private int DoSomething()
{
if (isRunning)
{
counter++;
return counter * 10;
}
}
}

使用 Harmony 注释进行修补

using SomeGame;
using HarmonyLib;

public class MyPatcher
{
// make sure DoPatching() is called at start either by
// the mod loader or by your injector

public static void DoPatching()
{
var harmony = new Harmony("com.example.patch");
harmony.PatchAll();
}
}

[HarmonyPatch(typeof(SomeGameClass))]
[HarmonyPatch("DoSomething")]
class Patch01
{
static FieldRef<SomeGameClass,bool> isRunningRef =
AccessTools.FieldRefAccess<SomeGameClass, bool>("isRunning");

static bool Prefix(SomeGameClass __instance, ref int ___counter)
{
isRunningRef(__instance) = true;
if (___counter > 100)
return false;
___counter = 0;
return true;
}

static void Postfix(ref int __result)
{
__result *= 2;
}
}

或者,使用反射手动修补

using SomeGame;
using System.Reflection;
using HarmonyLib;

public class MyPatcher
{
// make sure DoPatching() is called at start either by
// the mod loader or by your injector

public static void DoPatching()
{
var harmony = new Harmony("com.example.patch");

var mOriginal = typeof(SomeGameClass).GetMethod("DoSomething", BindingFlags.Instance | BindingFlags.NonPublic);
var mPrefix = typeof(MyPatcher).GetMethod("MyPrefix", BindingFlags.Static | BindingFlags.Public);
var mPostfix = typeof(MyPatcher).GetMethod("MyPostfix", BindingFlags.Static | BindingFlags.Public);
// add null checks here

harmony.Patch(mOriginal, new HarmonyMethod(mPrefix), new HarmonyMethod(mPostfix));
}

public static void MyPrefix()
{
// ...
}

public static void MyPostfix()
{
// ...
}
}

关于c# - 动态替换 C# 方法的内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7299097/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com