Context:
Program Structure Types
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.
A delegate is a class that can hold a reference to a method and, in the case of an instance method, an object on which that method can be invoked.
Once an instance of a delegate class has been created, it can be invoked, at which point the referenced method is invoked on the stored instance.
A delegate type can only hold references to methods of a particular signature, so creating and invoking delegates is type safe.
To define a delegate in COBOL, you use the DELEGATE-ID keyword, and the PROCEDURE DIVISION header to specify the signature. For example, to declare a delegate that has a string parameter and returns a binary-long:
delegate-id MyDelegate. procedure division using by value s1 as string returning n1 as binary-long. end delegate.
A method group is a method invocation expression preceded by the keyword METHOD, and without any parameters. When a method group is specified, it denotes one of the set of methods with the specified name in the specified class.
An implicit conversion exists between a method group and a specific delegate type, if one of the methods identified by the method group has a signature which is compatible with that delegate type.
For example, to create an instance of the delegate MyDelegate you declared above, that points to a suitable method:
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.
In the example above, the SET statement automatically selects the first overload of the method m as it has a signature which is compatible with that of the delegate type MyDelegate.
A method group can also be used as a parameter to a method, when that method has an argument of a compatible delegate type. Again, an implicit conversion from the method group to the delegate type will take place, and that delegate will be passed as the parameter to the target method.
You can set a delegate to point to a piece of code, without formally setting that code up as a specific method. Such a piece of code is called an Anonymous Method, and you use the keywords DELEGATE and END-DELEGATE to specify it.
You specify any parameters and return values with the words USING and RETURNING attached to the word DELEGATE.
The following shows how you can attach an anonymous method to the delegate MyDelegate you created in the previous examples:
01 d type MyDelegate. set d to delegate using s1 as string returning n1 as binary-long set n1 to s1::Length end-delegate.
To invoke a delegate after it has been created, you use the keyword RUN followed by the delegate name, and any parameters enclosed in parentheses. For example, to run the delegate d created above and displaying the results, type:
display run d("Hello")
This statement causes the anonymous method to be invoked and return a value of 5. This value is then displayed.
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.
A single delegate can hold references to more than one object/method pair. In such cases, at the time when the delegate is invoked, each one of the methods will be invoked in the order in which they were added to the delegate.
For example:
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.
An event is a way in which a class can provide notifications when something of interest happens, such as a key being pressed in a GUI environment.
To define an event, you add the keyword EVENT to a delegate field. For example:
01 ChangeEvent type ChangeDelegate event public.
Delegates of the same type, or compatible method groups or anonymous methods can be attached to the event, and will be invoked at the time when the owning class of the event invokes the backing delegate.
The relationship between an event and its backing delegate is very similar to the relationship between a property and its backing storage.
You use the ATTACH statement to attach a delegate, method group or an anonymous method to an event:
ATTACH {delegate-instance} TO event-expression {method-group} {anonymous-method}
For example:
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
To detach a delegate or method group from an event use the DETACH statement:
DETACH {delegate-instance} FROM event-expression {method-group}
For example:
detach MyDelegate from names::ChangeEvent detach method ListChanged from names::ChangeEvent