いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

System.Reflection.Emitについて (1)(HelloWorldみたいなもの) (AssemblyBuilder, ModuleBuilder, TypeBuilder, ILGenerator, MSIL)


以下、自分のメモ書きです。


System.Reflection.Emit名前空間に存在するクラス達は、実行時に動的に型を生成するための
機能を持ちます。同じように実行時生成としては、CodeDOMもありますが、Emitは型を特定の
アセンブリ上に動的に生成する際に、直接ILコードを設定します。なのでコンパイルする手順が
省けます。その代わり、ある程度のILコードに対して知識が必要となります。


でも、実際には細かくしっていなければならない訳では決してなく(実は私もほとんど分かりません(笑))
以下のようにして、どんなILコードを設定すればいいのかを予め調べておきます。

  1. サンプルソースを記述する。
  2. 一旦それをビルド。
  3. 生成されたアセンブリをildasm.exeに掛けて、どんなオペコードが吐かれるのかを確認。
  4. 同じオペコードを実際に動的生成する型に対して設定する。


上記のようにすると、シンプルなクラスの場合は結構簡単です。
私の場合は、DevExpress社が提供しているXPOというO/Rマッピングライブラリにて
利用するクラスを動的生成したかっただけなので、これで事足りそうな感じがしています。


実際の各クラスの役割や使い方などはMSDNの方が断然詳しいのでそれは割愛しておいて
サンプルみた方が分かりやすいと思います。


以下、サンプルです。

#region Emitのサンプル

    public class EmitSample : IExecutable{

        public interface ISayHello{
            void SayHello();
        }

        public void Execute(){
            //
            // 0.これから作成する型を格納するアセンブリ名作成.
            //
            AssemblyName    asmName        = new AssemblyName{Name="DynamicTypes"};
            //
            // 1.AssemlbyBuilderの生成
            //
            AppDomain       domain         = AppDomain.CurrentDomain;
            AssemblyBuilder asmBuilder     = domain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run); 
            //
            // 2.ModuleBuilderの生成.
            //
            ModuleBuilder   modBuilder     = asmBuilder.DefineDynamicModule("HelloWorld");
            //
            // 3.TypeBuilderの生成.
            //
            TypeBuilder     typeBuilder    = modBuilder.DefineType("SayHelloImpl", TypeAttributes.Public, typeof(object), new Type[]{typeof(ISayHello)});
            //
            // 4.MethodBuilderの生成
            //
            MethodAttributes methodAttr    = (MethodAttributes.Public | MethodAttributes.Virtual);
            MethodBuilder    methodBuilder = typeBuilder.DefineMethod("SayHello", methodAttr, typeof(void), new Type[]{});
            typeBuilder.DefineMethodOverride(methodBuilder, typeof(ISayHello).GetMethod("SayHello"));
            //
            // 5.ILGeneratorを生成し、ILコードを設定.
            //
            ILGenerator il = methodBuilder.GetILGenerator();
            il.Emit(OpCodes.Ldstr, "Hello World");
            il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[]{ typeof(string) }));
            il.Emit(OpCodes.Ret);
            //
            // 6.作成した型を取得.
            //
            Type type = typeBuilder.CreateType();
            //
            // 7.型を具現化.
            //
            ISayHello hello = (ISayHello) Activator.CreateInstance(type);
            //
            // 8.実行.
            //
            hello.SayHello();
        }
    }
#endregion


上記のものも、作成する際は一旦ISayHelloインターフェースを実装したクラスをサンプルとして
作成してビルドし、そのILコードを確認してからそのままEmitしただけです。


参考にしているリソースは以下です。