[C#] Harmonyでメソッドの動作を変更する 

## Harmonyについて

https://github.com/pardeike/Harmony

Harmonyは、実行中のメモリ上のプログラムにパッチを当てて、メソッドの動作を変更したり、戻り値を書き換えたりする事が出来るライブラリです。
主にゲームのMOD作成用途で使用される事が多いようですが、単体テスト等にも使えるようです。

## インストール
まずはNugetでLib.Harmonyをインストールします。今回の記事では、2.3.3 を使用することにします。

![NuGet](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519547/ef17f45d-a7c5-9ccd-4563-16ad52d395b9.png)

## パッチの当て方

### HamonyPatch属性を付与して一括でパッチを当てる方法
Harmony.PatchAll() で、HamonyPatch属性を指定したクラス・メソッドにまとめてパッチを当てる事が出来ます。パッチを当てる対象が常に固定なら、この方法が簡単だと思います。

```cs
using HarmonyLib;
using System;

public class Dog
{
    public string GetName() => "ハチ";
}

[HarmonyPatch(typeof(Dog))]
public class DogPatch
{
    [HarmonyPatch(nameof(Dog.GetName)), HarmonyPostfix]
    public static void GetNamePostfix(ref string __result) => __result  = "タロウ"; // 戻り値を"タロウ"に書き換え
}

public class Program
{
    public static void Main()
    {
        var harmony = new Harmony("MyPatch");
        harmony.PatchAll();

        var dog  = new Dog();
        Console.WriteLine($"犬の名前は{dog.GetName()}です");
        
        Console.ReadLine();
    }
}
```

### リフレクションで取得したMethodInfoでパッチを当てる方法
Harmony.Patch() で、リフレクションで取得した個別のメソッドにパッチを当てます。パッチ適用のタイミングや対象を動的に制御したい場合はこちら。

```cs
using HarmonyLib;
using System;

public class Dog
{
    public string GetName() => "ハチ";
}

public class DogPatch
{
    public static void GetNamePostfix(ref string __result) => __result  = "タロウ";
}

public class Program
{
    public static void Main()
    {
        var getNameOriginal = typeof(Dog).GetMethod(nameof(Dog.GetName));
        var getNamePostfix = typeof(DogPatch).GetMethod(nameof(DogPatch.GetNamePostfix));

        var harmony = new Harmony("MyPatch");
        harmony.Patch(original: getNameOriginal, postfix: getNamePostfix);

        var dog  = new Dog();
        Console.WriteLine($"犬の名前は{dog.GetName()}です");
        
        Console.ReadLine();
    }
}
```

### ファクトリーメソッドで取得したMethodInfoでパッチを当てる方法
引数がMethodBase originalMethod、戻り値がMethodInfoのファクトリーメソッドから、目的の動作をさせるメソッドを返してパッチを当てる方法です。DynamicMethodで作成したメソッド等を使う事も出来るらしいですが、やや敷居は高そうです。

```cs
using HarmonyLib;
using System;
using System.Reflection;

public class Dog
{
    public string GetName() => "ハチ";
}

public class DogPatch
{
    public static MethodInfo GetNamePostfixFactory(MethodBase originalMethod)
    {
        return typeof(DogPatch).GetMethod(nameof(GetNamePostfix));
    }

    public static void GetNamePostfix(ref string __result) => __result = "タロウ";
}

public class Program
{
    public static void Main()
    {
        var getNameOriginal = typeof(Dog).GetMethod(nameof(Dog.GetName));
        var getNamePostfixFactory = typeof(DogPatch).GetMethod(nameof(DogPatch.GetNamePostfixFactory));

        var harmony = new Harmony("MyPatch");
        harmony.Patch(original: getNameOriginal, postfix: new HarmonyMethod(getNamePostfixFactory));

        var dog = new Dog();
        Console.WriteLine($"犬の名前は{dog.GetName()}です");

        Console.ReadLine();
    }
}
```

## 基本的な使い方

### 戻り値を書き換える
__result は、元メソッドの実行結果が格納される特殊なパラメータで、書き換えるとその値がそのまま呼び出し元に返されます。
下記の例では、Postfixメソッド（元メソッドの後に実行される）で戻り値の書き換えを行っています。

```cs
using HarmonyLib;
using System;

public class Dog
{
    public string GetName() => "ハチ";
}

[HarmonyPatch(typeof(Dog))]
public class DogPatch
{
    // __result に格納された戻り値を "タロウ" に書き換え
    [HarmonyPatch(nameof(Dog.GetName)), HarmonyPostfix]
    public static void GetNamePostfix(ref string __result) => __result  = "タロウ";
}

public class Program
{
    public static void Main()
    {
        var harmony = new Harmony("MyPatch");
        harmony.PatchAll();

        var dog  = new Dog();
        Console.WriteLine($"犬の名前は{dog.GetName()}です");
        
        Console.ReadLine();
    }
}
```

```:実行結果
犬の名前はタロウです
```

### インスタンスにアクセスする
__instance パラメータは、インスタンスメソッドのインスタンスが格納される特殊なパラメータです。
下記の例では、Prefixメソッド（元メソッドの前に実行される）でプロパティを書き換えています。

```cs
using HarmonyLib;
using System;
using System.Reflection;

public class Dog
{

    public string Name { get; set; } = "ハチ";

    public void DoSomething() => Console.WriteLine($"犬の名前は{Name}です");
}

[HarmonyPatch(typeof(Dog))]
public class DogPatch
{
    // DoSomething実行前に、Dog.Name を "タロウ" に書き換え
    [HarmonyPatch(nameof(Dog.DoSomething)), HarmonyPrefix]
    public static void DoSomethingPrefix(Dog __instance)
        => __instance.Name = "タロウ";
}


public class Program
{
    public static void Main()
    {
        var harmony = new Harmony("MyPatch");
        harmony.PatchAll();

        var dog = new Dog();
        dog.DoSomething();

        Console.ReadLine();
    }
}
```

```:実行結果
犬の名前はタロウです
```

### メソッドを独自の処理に置き換える
Prefixメソッドの戻り値をboolにし、falseを返す事で元メソッドの処理をスキップする事が出来ます。
下記の例では、x + y を x * y に置き換えています。

```cs
using HarmonyLib;
using System;

public class Calc
{
    public int Add(int x, int y) => x + y;
}

[HarmonyPatch(typeof(Calc))]
public class CalcPatch
{
    // 計算結果を x * y に変更し、元メソッドは実行しない
    [HarmonyPatch(nameof(Calc.Add)), HarmonyPrefix]
    public static bool AddPrefix(int x, int y, ref int __result)
    {
        __result = x * y;
        return false;
    }
}

public class Program
{
    public static void Main()
    {
        var harmony = new Harmony("MyPatch");
        harmony.PatchAll();

        var calc = new Calc();
        Console.WriteLine(calc.Add(5, 10));

        Console.ReadLine();
    }
}
```

例で使用した __result と __instance の他にも色々なパラメータが使えますが、公式のドキュメントを参照してください。

https://harmony.pardeike.net/articles/patching-injections.html