C#で動的にインターフェイスを実装するクラスを作成する（メソッド呼び出し）

# C#で動的にインターフェイスを実装するクラスを作成する（メソッド呼び出し）

## はじめに

[前回記事](https://qiita.com/mngreen/items/9848f02dc72796c19d32)の続きです。

前回はGetter, Setterを持つシンプルなI/Fを実装する例でしたが、今回はメソッドを持つI/Fを実装します。そして、そのメソッドは別で実装されたメソッドを呼び出します（ベタ書きですが、疑似的なモック化を狙っています）。

以下、全コードのサンプルになります。

## サンプル

<details><summary>IMyMockTypeを実現するMyMockTypeを作成、インスタンス化する</summary><div>

```csharp
using System.Reflection;
using System.Reflection.Emit;

namespace DemoAssemblyBuilder
{
    /// <summary>
    /// Mock検証用インターフェイス
    /// </summary>
    public interface IMyMockType
    {
        /// <summary>
        /// 計算結果を返します
        /// </summary>
        /// <param name="number">入力値</param>
        /// <returns>計算結果</returns>
        int Calculate(int number);
    }

    public class Program
    {
        static void Main(string[] args)
        {
            // 以下のクラスを動的に作成します
            /*
            namespace MyExample
            {
                public class MyMockType : IMyMockType
                {
                    public int Calculate(int number)
                    {
                        return ...置き換えた処理を実行します...;
                    }
                }
            }
            */

            // アセンブリの定義
            AssemblyName assemblyName = new AssemblyName("MyExample");
            AssemblyBuilder assemblyBuilder =
                AssemblyBuilder.DefineDynamicAssembly(
                    assemblyName,
                    AssemblyBuilderAccess.Run);

            // モジュールの定義
            // モジュール名は多くの場合アセンブリ名と同じにすると良い
            ModuleBuilder moduleBuilder =
                assemblyBuilder.DefineDynamicModule(assemblyName.Name!);

            // 型情報の定義
            // 以降、MyMockTypeを作っていきます
            TypeBuilder typeBuilder = moduleBuilder.DefineType(
                "MyMockType",
                 TypeAttributes.Public);

            // IMyMockTypeを実現します
            Type myInterfaceType = typeof(IMyMockType);
            typeBuilder.AddInterfaceImplementation(myInterfaceType);

            // Calculateメソッドを定義します
            MethodBuilder meth = typeBuilder.DefineMethod(
                "Calculate",
                MethodAttributes.Public | MethodAttributes.Virtual,
                typeof(int),
                new Type[] { typeof(int) });

            // 引数を受取り、その引数を呼び出すメソッドの引数に渡します
            ILGenerator methIL = meth.GetILGenerator();
            methIL.Emit(OpCodes.Ldarg_1);
            var methodInfo = typeof(Program).GetMethod(nameof(MockFunc), BindingFlags.Static | BindingFlags.Public);
            methIL.Emit(OpCodes.Call, methodInfo!);
            methIL.Emit(OpCodes.Ret);

            typeBuilder.DefineMethodOverride(meth, typeof(IMyMockType).GetMethod("Calculate")!);

            // 以降、動作確認
            Type myMockType = typeBuilder.CreateType()!;
            IMyMockType mock = (IMyMockType)Activator.CreateInstance(myMockType)!;
            Console.WriteLine($"mock.Calculate : " + mock.Calculate(3));
            Console.ReadKey();
        }

        /// <summary>
        /// モック化したい処理の実装です
        /// </summary>
        /// <param name="number">入力値</param>
        /// <returns>計算結果</returns>
        public static int MockFunc(int number)
        {
            return number * number;
        }
    }
}

```
</div></details>

## 個人的な学習ポイント

- OpCodes.Callの場合、MethodInfoを引数に渡して呼び出すことができる（他にもOpCodes.Callvirt, OpCodes.Newobjも大丈夫そうなのですが、他のOpCodesの意味は分かってないです）
- [前回記事](https://qiita.com/mngreen/items/9848f02dc72796c19d32)ではGetter, Setterで毎回```methIL.Emit(OpCodes.Ldarg_0);```と書いていたのでお約束的に必要なのかと思ったら、呼び出し元（this）が必要な場合のみ書くらしい

## 終わりに

普段は内部ロジックなんて書かないのですが、一回書いてみると新しい発見が見つかりますね。まだまだMoqのようにはいきませんが、MethodInfoを渡せることを知ったので、ラムダ関数の書き方で書いたActionやFuncをメソッドの引数に渡し、そこからMethodInfoを抽出すればMoqのようなことができそうな気がします。