デリゲートとイベント

デリゲートは、オブジェクト指向の手続きポインターに相当し、型の安全性を保つソリューションを提供します。デリゲートは EVENT キーワードと組み合わせて使用し、一般的には、GUI 環境でキーが押された場合など、発生したイベントについて他のソフトウェア コンポーネントに通知するためのメカニズムとして使用します。

コンテキスト:

    プログラム構造
        Types

delegate-specification

delegate-header procedure-division-header

delegate-header

メソッド署名メソッドの署名を使用すると、特定の要素のヘッダー内で、渡すパラメーターおよび戻り項目を指定できます。この署名を使用する場合は、メソッド内の手続き部のヘッダーを省きます。 access-modifier attribute-clause

delegate-id MessageHandler (str as string).
end delegate.

class-id DelegatesEvents.

01 MessageArrived     event type MessageHandler static.

method-id Main static.
  *> explicit delegate construction
  invoke new MessageHandler(MyHandler)

  *> implicit delegate construction
  declare handler as type MessageHandler = method MyHandler

  *> subscribe to an event
  attach method MyHandler to MessageArrived

  *> raise the event
  invoke MessageArrived("Test message")

  *> unsubscribe from the event
  detach method MyHandler from MessageArrived

  *> Throws a null reference exception as there are no subscribers
  invoke MessageArrived("Test message 2")

  *> Safely raising an #event
  declare handler2 as type MessageHandler = MessageArrived
  if handler2 not equals null
     invoke handler1("Safe message")
  end-if

end method.

method-id MyHandler static (str as string).
  display str
end method.

end class.

その他の情報

デリゲートはメソッドへの参照を保持するクラスであり、インスタンス メソッドの場合は、そのメソッドを呼び出せるオブジェクトとなります。

デリゲート クラスのインスタンスが作成されると、保存されたインスタンス上の参照先メソッドが呼び出される場所で呼び出せるようになります。

1 つのデリゲート型に保持できるのは特定の署名のメソッドへの参照のみであるため、デリゲートの作成と呼び出しにおいて型の安全性が保たれます。

COBOL でデリゲートを定義する際には、DELEGATE-ID キーワードと、署名を指定する PROCEDURE DIVISION ヘッダーを使用します。たとえば、文字列パラメーターを持ち、binary-long を返すデリゲートは、次のように宣言します。

delegate-id MyDelegate.
procedure division using by value s1 as string returning n1 as binary-long.
end delegate.
メソッド グループ

メソッド グループは、キーワード METHOD を前に置くメソッド呼び出し式であり、パラメーターは取りません。メソッド グループが指定された場合、それは複数のメソッドのうち、指定された名前を持つ、指定されたクラスのメソッドを示します。

メソッド グループと特定のデリゲート型の間では暗黙変換が行われますが、メソッド グループで指定されたメソッドが、そのデリゲート型と互換性のある署名を持つ場合に限ります。

たとえば、上記で宣言したデリゲート MyDelegate のインスタンスを作成し、適切なメソッドを示すようにするには、次のように記述します。

01 d type MyDelegate.
01 c type Class1.
...
set d to method c::m

class-id Class1.
method-id m.
procedure division using by value s1 as string returning n1 as binary-long.
end method.

method-id m.
procedure division using by value s1 as string s2 as string returning n1 as binary-long.
end method.

end class.

上記の例では、SET 文はメソッド m の最初のオーバーロードを自動的に選択します。これは、デリゲート型 MyDelegate の署名と互換性のある署名を持っているためです。

メソッド グループはメソッドのパラメーターとして使用することもできますが、そのメソッドが互換性のあるデリゲート型の引数を持つ場合に限ります。この場合にも、メソッド グループからデリゲート型への暗黙変換が行われ、そのデリゲートは対象メソッドにパラメーターとして渡されます。

匿名メソッド

デリゲートがコードの一部分をポイントするよう設定できます。その際、コードを正式にメソッドにする必要はありません。このようなコードは匿名メソッドと呼ばれ、キーワード DELEGATE および END-DELEGATE を使用して指定できます。

DELEGATE に語 USING および RETURNING を付加して、パラメーターの指定と値を返します。

次の例は、前述の例で作成したデリゲート MyDelegate に匿名メソッドを付加できることを示します。

01 d type MyDelegate.
set d to delegate using s1 as string
                                returning n1 as binary-long
                  set n1 to s1::Length
              end-delegate.
デリゲートの呼び出し

デリゲートを作成した後で削除するには、キーワード RUN を使用し、後に続けてデリゲート名およびかっこで囲んだパラメーターを記述します。たとえば、上記で作成したデリゲート d を実行して結果を表示するには、次のように入力します。

display run d("Hello")

この文によって匿名メソッドが呼び出され、値 5 が返されます。その後、この値が表示されます。

デリゲートの作成と呼び出しの例
       delegate-id MyDelegate.
       procedure division using by value s1 as string returning n1 as binary-long.
       end delegate.

       class-id a.
       method-id main static.
       01 d type MyDelegate.
       01 c type Class1 value new Class1.


       set d to method c::m
       display run d("ABC")

       set d to delegate using s1 as string
                         returning n1 as binary-long
                    set n1 to s1::Length
                end-delegate
       display run d("XYZ")
       end method.
       end class.

       class-id Class1.
       method-id m.
       procedure division using by value s1 as string returning n1 as binary-long.
           set n1 to size of s1 + 3
       end method.
       method-id m.
       procedure division using by value s1 as string s2 as string returning n1 as binary-long.
       end method.
       end class.
デリゲートの結合

1 つのデリゲートで複数のオブジェクト/メソッド ペアに対する参照を保持できます。このような場合、デリゲートが呼び出されると、各メソッドはデリゲートに追加された順序で呼び出されます。

  • メソッド グループ、匿名メソッド、またはその他のデリゲートをデリゲートに追加するには、演算子「+」を使用する。
  • デリゲートからメソッドを削除するには、演算子「-」を使用する。
注: こうした式では、少なくとも右辺または左辺のいずれかをデリゲート型のインスタンスにする必要があります。

次に例を示します。

       class-id DelegateCombining.
       method-id main static.
       01 cd1 type D.
       01 cd2 type D.
       01 cd3 type D.
       01 c type C value new C.
           set cd1 to method type C::M1
           invoke cd1(-1)
           set cd2 to method type C::M2
           invoke cd2(-2)
           set cd3 to cd2 + method type C::M1
           invoke cd3(10)
           set cd3 to method type C::M1 + cd3
           invoke cd3(20)

           set cd3 to cd3 + method c::M3
           invoke cd3(30)

           set cd3 to cd3 - method type C::M1
           invoke cd3(40)
           set cd3 to cd3 - method c::M3
           invoke cd3(50)
           set cd3 to cd3 - method type C::M2
           invoke cd3(60)
           set cd3 to cd3 - cd2
           invoke cd3(60)
           set cd3 to cd3 - cd1
           try
               invoke cd3(70)
           catch
               display "null reference exception caught"
           end-try
           set cd3 to cd3 - cd1
 
           set cd1 to method type C::M1
           set cd2 to method type C::M2
           set cd3 to cd1 + cd2 + cd2 + cd1
           invoke cd3(80)
           set cd3 to cd3 - cd1
           invoke cd3(90)
           set cd3 to cd1 + cd2 + cd2 + cd1
           set cd3 to cd3 - (cd1 + cd2)
           invoke cd3(100)
           set cd3 to cd1 + cd2 + cd2 + cd1
           set cd3 to cd3 - (cd2 + cd2)
           invoke cd3(110)
           set cd3 to cd1 + cd2 + cd2 + cd1
           set cd3 to cd3 - (cd2 + cd1)
           invoke cd3(120)
           set cd3 to cd1 + cd2 + cd2 + cd1
           set cd3 to cd3 - (cd1 + cd1)
           invoke cd3(130)

       end method.
       end class.

       delegate-id D.
       procedure division using by value i as binary-long.
       end delegate.

       class-id C.
       method-id M1 static.
       procedure division using by value i as binary-long.
           display "C::M1 --> " i
       end method.
       method-id M2 static.
       procedure division using by value i as binary-long.
           display "C::M2 --> " i
       end method.
       method-id M3.
       procedure division using by value i as binary-long.
           display "C::M3 --> " i
       end method.
       end class.

イベント

イベントは、GUI 環境でキーが押されたなどの重要な事象の発生をクラスが通知するための手段です。

イベントを定義するには、キーワード EVENT をデリゲートのフィールドに追加します。次に例を示します。

       01 ChangeEvent type ChangeDelegate event public.

同じ型のデリゲート、または互換性のあるメソッド グループや匿名メソッドはイベントにアタッチできます。イベントを所有するクラスがバッキング デリゲートを呼び出す際に、これらは呼び出されます。

イベントとそのバッキング デリゲートの関係は、プロパティとバッキング記憶域の関係に非常に似ています。

イベントへのメソッドまたはデリゲートのアタッチ

デリゲート、メソッド グループ、または匿名メソッドをイベントにアタッチするには、次のように ATTACH 文を使用します。

ATTACH {delegate-instance}    TO event-expression
                {method-group}
                {anonymous-method}

次に例を示します。

       01 names type myList.
       01 MyDelegate type ChangeDelegate.
       procedure division.
           set names to new myList
           attach method ListChanged to names::ChangeEvent
           set MyDelegate to method ListChanged
           attach MyDelegate to names::ChangeEvent
イベントからのメソッドまたはデリゲートのデタッチ

デリゲートまたはメソッド グループをイベントからデタッチするには、次のように DETACH 文を使用します。

DETACH {delegate-instance}    FROM  event-expression
                {method-group}

次に例を示します。

           detach MyDelegate from names::ChangeEvent
           detach method ListChanged from names::ChangeEvent