コンストラクタ・イベント

コンストラク

インスタンスコンストラク
  • .ctor
  • 参照型の場合、引数なしのコンストラクタは自動的に生成される
  • インライン初期化したフィールドは、コンパイル時にインスタンスコンストラクタコードに自動変換される。
    • コンストラクタの数だけ重複した初期化ロジックが生成されることになるので、フィールドは宣言だけにとどめるほうがコードサイズの削減になる。
    class FooClass
    {
        private int a_;
        private int b_ = 10;

        public FooClass(int b)
        {
            b_ = b;
        }
        public FooClass(int a, int b)
        {
            a_ = a;
            b_ = b;
        }
    }

試しにこういうクラスのILコードを覗く。

.method public hidebysig specialname rtspecialname 
        instance void  .ctor(int32 b) cil managed
{
  // コード サイズ       25 (0x19)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.s   10
  IL_0003:  stfld      int32 ConsoleApplication3.FooClass::b_
  IL_0008:  ldarg.0
  IL_0009:  call       instance void [mscorlib]System.Object::.ctor()
  IL_000e:  nop
  IL_000f:  nop
  IL_0010:  ldarg.0
  IL_0011:  ldarg.1
  IL_0012:  stfld      int32 ConsoleApplication3.FooClass::b_
  IL_0017:  nop
  IL_0018:  ret
} // end of method FooClass::.ctor
.method public hidebysig specialname rtspecialname 
        instance void  .ctor(int32 a,
                             int32 b) cil managed
{
  // コード サイズ       32 (0x20)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.s   10
  IL_0003:  stfld      int32 ConsoleApplication3.FooClass::b_
  IL_0008:  ldarg.0
  IL_0009:  call       instance void [mscorlib]System.Object::.ctor()
  IL_000e:  nop
  IL_000f:  nop
  IL_0010:  ldarg.0
  IL_0011:  ldarg.1
  IL_0012:  stfld      int32 ConsoleApplication3.FooClass::a_
  IL_0017:  ldarg.0
  IL_0018:  ldarg.2
  IL_0019:  stfld      int32 ConsoleApplication3.FooClass::b_
  IL_001e:  nop
  IL_001f:  ret
} // end of method FooClass::.ctor

両方のコンストラクタにフィールドb_初期化のILコードが生成されている。


  • 値型の場合、多くのコンパイラは、引数なしのコンストラクタが定義されていても呼び出すコードを出力しない
  • 値型の場合、コンストラクタで全てのフィールドを初期化しなければならない
タイプコンストラク
  • CLRはタイプコンストラクタが一度だけ実行されることを保証する。(このとき、同期用のロックを使用する)
  • ハンドルされない例外をスローした場合、その型のフィールド・メソッドにアクセスするとSystem.TypeInitializationExceptionがスローされる。
  • 評価の流れ
    • AppDomainにおいて型が参照されているかを確認
    • タイプコンストラクタが既に実行されているかを確認
    • 実行されていなかった場合、ネイティブコードを生成し、メソッドのネイティブコード内に出力する
JITコンパイラによるタイプコンストラクタ呼出の挿入

2つの選択肢がある。

  • preciseセマンティクス
    • CLRが正確に決められたタイミングで呼び出す。
  • before-field-initセマンティクス
    • 継承されないstaticフィールドへのアクセス実行前のどこかに挿入。
    • 呼び出すタイミングとして自由度が高く、CLRにとって高速に実行できるコードが生成できる。

規定ではコンパイラによってセマンティクスは決定される。
判断結果として、型定義メタデータにbeforefieldinitメタデータフラグを付ける。(これによりCLRへ通知する)
staticフィールドのインライン初期化のみをおこなっている場合、対象の型にbeforefieldinitメタデータフラグが付与される。
一方で明示的なタイプコンストラクタを使用している場合はbeforefieldinitフラグは付与されない。これは、タイプコンストラクタには任意のコードを含めることができ、副作用が発生する可能性もあるため、決められたタイミングで実行しないといけないため。

イベント

  • デリゲートで実現されている。
    • デリゲートは、タイプセーフなコールバックメソッド
  • eventキーワードの指定によって以下の三点が生成される
    • プライベートなデリゲートフィールド
    • メソッドadd_Event
      • Delegate#Combineが呼び出される
      • "+="演算子呼出はこのメソッドを呼び出す。
    • メソッドremove_Event
      • Delegate#Removeが呼び出される
      • "-="演算子はこのメソッドを呼び出す。
      • 指定されたハンドラと同じメソッドを持つハンドラを削除する。
  • イベントの追加/削除はスレッドセーフ。
  • ロックにはイベントハンドラオブジェクト自身が利用される
    public class Class1
    {
        event EventHandler Event;
    }

というクラスをILで確認したら、

.class public auto ansi beforefieldinit ConsoleApplication1.Class1
       extends [mscorlib]System.Object
{
  .field private class [mscorlib]System.EventHandler Event
  .method private hidebysig specialname instance void 
          add_Event(class [mscorlib]System.EventHandler 'value') cil managed synchronized
  {
    // コード サイズ       24 (0x18)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldarg.0
    IL_0002:  ldfld      class [mscorlib]System.EventHandler ConsoleApplication3.Class1::Event
    IL_0007:  ldarg.1
    IL_0008:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                            class [mscorlib]System.Delegate)
    IL_000d:  castclass  [mscorlib]System.EventHandler
    IL_0012:  stfld      class [mscorlib]System.EventHandler ConsoleApplication3.Class1::Event
    IL_0017:  ret
  } // end of method Class1::add_Event

  .method private hidebysig specialname instance void 
          remove_Event(class [mscorlib]System.EventHandler 'value') cil managed synchronized
  {
    // コード サイズ       24 (0x18)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldarg.0
    IL_0002:  ldfld      class [mscorlib]System.EventHandler ConsoleApplication3.Class1::Event
    IL_0007:  ldarg.1
    IL_0008:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,
                                                                                           class [mscorlib]System.Delegate)
    IL_000d:  castclass  [mscorlib]System.EventHandler
    IL_0012:  stfld      class [mscorlib]System.EventHandler ConsoleApplication3.Class1::Event
    IL_0017:  ret
  } // end of method Class1::remove_Event

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // コード サイズ       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Class1::.ctor

  .event [mscorlib]System.EventHandler Event
  {
    .addon instance void ConsoleApplication3.Class1::add_Event(class [mscorlib]System.EventHandler)
    .removeon instance void ConsoleApplication3.Class1::remove_Event(class [mscorlib]System.EventHandler)
  } // end of event Class1::Event
}

となっていた。