Rolling Out the Big Guns - Micro Focus Managed COBOL Does Generic Delegates
Delegates are a managed code way of implementing methods by reference. In C, these things are called function pointers. However, function pointers are just plane dangerous (they cause things to crash and lack much in the way of type safety). So, generic delegates give the ability to reference methods as variables (local or working storage items), or in line, with all the type safety we expect from a modern language like Managed COBOL.
I have previously discussed anonymous delegates here. In this post, I want to make a COBOL example using a named generic delegate which is very similar to the VB and C# examples on MSDN. So here it is:
$set ilusing(System.Collections.Generic)
class-id sorter.
method-id main static.
01 dinosaurs type List[String] value New List[String].
invoke dinosaurs::Add("Pachycephalosaurus")
invoke dinosaurs::Add("Amargasaurus")
invoke dinosaurs::Add("")
invoke dinosaurs::Add(Null)
invoke dinosaurs::Add("Mamenchisaurus")
invoke dinosaurs::Add("Deinonychus")
display "Unsorted list:"
invoke self::Display(dinosaurs)
display "Sort with generic Comparison(Of String) delegate:"
invoke dinosaurs::Sort(new Comparison[String](self::CompareDinosByLength))
invoke self::Display(dinosaurs)
display "--=== END ===--"
end method.
method-id CompareDinosByLength static.
procedure division using by value
x as string y as string returning ret as binary-long.
evaluate true
when x = null and y = null move 0 to ret
when x = null and y not = null move -1 to ret
when y = null move 1 to ret
when other
move x::Length::CompareTo(y::Length) to ret
if ret equals 0
move x::CompareTo(y) to ret
end-if
end-evaluate
end method.
method-id Display private static.
01 s string.
procedure division using by value lis As type List[String].
display ""
perform varying s through lis
if s equals null
display "(Nothing)"
else
display '"' s '"'
end-if
end-perform
end method.
end class.
In the above code, we have defined a method called CompareDinosByLength
. We do not call this method directly anywhere in the code. What we actually do is construct a delegate to it via the code new Comparison[String](self::CompareDinosByLength)
. Because we create the delegate to CompareDinosByLength
with the generic argument [String]
, we are making a generic delegate. This is a very type safe approach to creating delegates because both the compiler and the runtime will make sure that the Comparison[String]
delegate can only be a delegate to a method which takes string
arguments. Generics are a very powerful way of enforcing signature type safety in Managed COBOL.
If we wanted to make a comparison to delegate for comparing Armadillo objects, this can also be done. The code would look like this: new Comparison[type Armadillo](self::CompareArmadillos)
. But if instead, we did this:new Comparison[type Armadillo](self::
CompareDinosByLength
)
, we would get a compile time error becauseCompareDinosByLength
has String
arguments, but we have defined the generic as being typed to have Armadillo arguments.
Here is what our Armadillo comparing method might look like:
* Compare Armadillos by tail length
method-id CompareArmadillosstatic.
procedure division using by value
x as type Armadillo y as type Armadillo
returning ret as binary-long.
evaluate true
when x.tailLength > y.tailLength move 1 to ret
when x.tailLength < y.tailLength move -1 to ret
when other move 0 to ret
end-evaluate
end method.
Footnote
Please note that in Managed COBOL, String
is a type and a COBOL data form. So...
01 s1 String.
01 s2 type String.
... resolve to the same thing but...
$set ilusing(SomeNamespace)
01 a1 type Armadillo
01 a2 Armadillo
... will not even compile because the line defining a2
does not use the 'type
' keyword.
CodeProject