C#でメソッドを動的に入れ替える

というのをやってみたら、動いちゃったので、共有です。

# サンプル
```csharp
using UnityEngine;
using System;
using System.Collections;
using System.Reflection;

public class ReplaceMethod1 : MonoBehaviour
{
    static void ExchangeFunctionPointer(MethodInfo method0, MethodInfo method1)
    {
        unsafe
        {
            var functionPointer0 = method0.MethodHandle.Value.ToPointer();
            var functionPointer1 = method1.MethodHandle.Value.ToPointer();
            var tmpPointer = *((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer());
            *((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer()) = *((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer());
            *((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer()) = tmpPointer;
        }
    }

    void Awake()
    {
        MethodInfo onGuiMethod = this.GetType().GetMethod("OnGUI", BindingFlags.NonPublic | BindingFlags.Instance);
        MethodInfo onGuiV2Method = this.GetType().GetMethod("OnGUIv2", BindingFlags.NonPublic | BindingFlags.Instance);
        ExchangeFunctionPointer(onGuiMethod, onGuiV2Method);
    }

    void OnGUI()
    {
        if (GUILayout.Button("This is OnGUIv1"))
        {
        }
    }

    void OnGUIv2()
    {
        if (GUILayout.Button("This is OnGUIv2"))
        {
        }
    }
}
```
### 実行結果
![スクリーンショット 2015-01-10 午後9.10.47.png](https://qiita-image-store.s3.amazonaws.com/0/5973/bfdbf293-8978-3039-e6a0-a597f283d8aa.png)
OnGUIの代わりに、OnGUIv2が呼び出されていますね。
### てきとーな解説
やっている事は、
1. AwakeでリフレクションでOnGUI,OnGUIv2メソッドを取得して、ExchangeFunctionPointerメソッドに二つを投げ込む
2. ExchangeFunctionPointerメソッド内では、てきとーに実際の処理の関数ポインタを付け替え
3. おわり

ポインタをごにょる関係上、unsafeだったりします。
この例では、二つのメソッドの処理を入れ替えていますが、これは絶対ではなく、ポインタを共有する事もできます。
iOS実機でも動いたのが、わりと衝撃でした。

# ただし、一度でも実際に呼び出したメソッドは、変更不能
これが致命傷...

```csharp
using UnityEngine;
using System;
using System.Collections;
using System.Reflection;

public class ReplaceMethod1 : MonoBehaviour
{
    static void ExchangeFunctionPointer(MethodInfo method0, MethodInfo method1)
    {
        unsafe
        {
            var functionPointer0 = method0.MethodHandle.Value.ToPointer();
            var functionPointer1 = method1.MethodHandle.Value.ToPointer();
            var tmpPointer = *((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer());
            *((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer()) = *((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer());
            *((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer()) = tmpPointer;
        }
    }

    void OnGUI()
    {
        if (GUILayout.Button("This is OnGUIv1"))
        {
            MethodInfo onGuiMethod = this.GetType().GetMethod("OnGUI", BindingFlags.NonPublic | BindingFlags.Instance);
            MethodInfo onGuiV2Method = this.GetType().GetMethod("OnGUIv2", BindingFlags.NonPublic | BindingFlags.Instance);
            ExchangeFunctionPointer(onGuiMethod, onGuiV2Method);        }
    }

    void OnGUIv2()
    {
        if (GUILayout.Button("This is OnGUIv2"))
        {
            MethodInfo onGuiMethod = this.GetType().GetMethod("OnGUI", BindingFlags.NonPublic | BindingFlags.Instance);
            MethodInfo onGuiV2Method = this.GetType().GetMethod("OnGUIv2", BindingFlags.NonPublic | BindingFlags.Instance);
            ExchangeFunctionPointer(onGuiMethod, onGuiV2Method);        }
    }
}
```
本来は、こうしたかったんですが、どうにも、一度ロードされたメソッドは、後からポインタを付け替えても意味がないようです？

# Debug.LogをApplication.RegisterLogCallback以外の方法でフックする用途など
```csharp
#if UNITY_EDITOR
using UnityEditor;
using System;
using System.Collections;
using System.Reflection;

[InitializeOnLoad]
public class ReplaceMethod2
{
    static ReplaceMethod2()
    {
        MethodInfo logMethod = typeof(Debug).GetMethod("Log", new System.Type[] { typeof(object) });
        MethodInfo logWarningMethod = typeof(Debug).GetMethod("LogWarning", new System.Type[] { typeof(object) });
        ExchangeFunctionPointer(logMethod, logWarningMethod);
        Debug.LogWarning("this is Warning");
        Debug.Log("this is Log");
    }

    static void ExchangeFunctionPointer(MethodInfo method0, MethodInfo method1)
    {
        unsafe
        {
            var functionPointer0 = method0.MethodHandle.Value.ToPointer();
            var functionPointer1 = method1.MethodHandle.Value.ToPointer();
            var tmpPointer = *((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer());
            *((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer()) = *((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer());
            *((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer()) = tmpPointer;
        }
    }
}
#endif
```
てきとーにDebug.LogとDebug.Warningを入れ替えてみてます。
![スクリーンショット 2015-01-10 午後10.09.45.png](https://qiita-image-store.s3.amazonaws.com/0/5973/d0a356e0-4cb6-ca60-fd12-af1a4bcf95a2.png)