This chapter describes method signatures and method overloading in .NET.
In .NET it is permissible (and very common practice) for a given class or interface to have multiple methods with the same name, which are distinguished from each other by having different signatures. The signature of a method is determined by the:
When code written in COBOL, or any other language, attempts to invoke a method in some class or interface, and more than one method with the given name exists, then a choice of the available methods is made. The choice is based on the number of parameters and the ‘best’ type match for the parameters (the precise definition of ‘best’ is complex and is treated later). If no method with a suitable signature can be found, a ‘method not found’ error results. If there is no method that provides a better match than all the other possible methods with this name, an ‘ambiguous match’ error is given.
Although the type of the RETURNING item is a part of the method’s signature, the choice of method to be invoked is never based on the RETURNING type. In fact, although .NET does allow multiple methods to exist in a class, where the methods differ only by returning type, this is not generally good practice, and COBOL will produce an error if it finds such multiple methods within a single class. The only time when such multiple methods can arise from a COBOL program is in the case of the Explicit and Implicit operators. See Operator-ID Paragraph.
class-id. A.
repository.
Class sys-datetime as “System.DateTime”.
static.
Method-id. Main.
01 obj-any object. *> define an item of type System.Object
01 obj-string string. *> define an item of type System.String
01 obj-datetime sys-datetime. *> define an item of type System.DateTime
Invoke self::”Method1” *> invokes variant 1
Invoke self::”Method1”(obj-any) *> invokes variant 2
Invoke self::”Method1”(obj-string) *> invokes variant 3
Invoke self::”Method1”(obj-datetime) *> invokes variant 2
*> Note that the last invoke maps onto variant 2 because an object of type
*> System.DateTime can be assigned to a System.Object but not to a System.String
End method main.
Method-id. Method1. *> Variant 1 (no parameters)
Procedure division.
…
End method method1.
Method-id. Method1. *> Variant 2 (object parameter)
Procedure division using by value o1 as object.
…
End method method1.
Method-id. Method1. *> Variant 3 (string parameter)
Procedure division using by value o1 as string.
…
End method method1.
End static.
End class a.
The type of a COBOL parameter may be specified by:
For REFERENCE and OUTPUT parameters, native .NET types are exposed directly as managed pointers to the corresponding .NET type. COBOL data types that do not correspond to native .NET types (such as PIC X fields, groups, numeric fields other than BINARY-LONG and so on) are exposed as COBOL pointers (objects of type MicroFocus.COBOL.Program.Reference).
For VALUE parameters, and RETURNING items, native .NET types are exposed as objects of the corresponding type.
Other COBOL data types used as VALUE parameters are exposed as follows:
See COBOL Type Compatibility for details of the correspondence between native .NET types and COBOL data types.
In this discussion, the term arguments means the set of parameters specified in the invoking method (for example obj01 in the statement INVOKE self::”Method1”(obj01)). The term parameters means the formal parameters specified in each method definition (in the PROCEDURE DIVISION USING header).
Method overload resolution takes place in two phases:
We start with the complete set of all methods with the required name that exist in the target class or in any of its parent classes (excluding those with identical signatures). From this set, any methods that are not visible from the invoking program, based on the visibility attribute encoded into the method, are discarded as follows:
| Attribute | Visibility |
|---|---|
| PRIVATE | Visible only if the calling class is the same as the target class |
| PUBLIC | Always visible |
| PROTECTED | Visible only if the calling class derives from the target class |
| INTERNAL | Visible only if the target class is in the same assembly as the calling class |
| PROTECTED INTERNAL | Visible only if the calling class derives from the target class, or the calling class is in the same assembly as the target |
Next, if the required method is an instance method (a method that is applied to an object instance), all static methods are discarded. Similarly, if the required method is a static method, all instance methods are discarded.
Now each method is examined in turn to see whether it is applicable in normal form. This is determined as follows:
For a REFERENCE or OUTPUT parameter, the type of the argument must be exactly the same as the parameter type.
For a VALUE parameter, an implicit conversion must exist from the type of the argument to the type of the corresponding parameter. If any of these implicit conversions require truncation, this method is said to be a ‘truncation match’. See Implicit Conversion.
If a method is not applicable according to these rules, an attempt is made to match the method in expanded form, if both the following are true:
The expanded form of the method is derived from the normal form by replacing the array parameter by zero or more value parameters of the same type as the array element, so that the total number of parameters is the same as the number of invoking arguments. If the class already contains a method in normal form with this same expanded signature, then this method is determined to be not applicable. Otherwise the expanded form is tested for applicability in the same way as the test for normal form above.
After the above selection procedure:
The general intent of the selection process is to produce the best match (in the sense of most specific match) for the given set of arguments. For example, suppose class A derives from class B, which itself derives from System.Object. The following example shows how, even in the case where the supplied argument is not exactly the same as that for any of the target methods, the “most specific” rule takes effect.
Class X.
Static.
Method-id. Method1.
Procedure division using by value arg1 as object. *> method (i)
End method method1.
Method-id. Method1.
Procedure division using by value arg1 as B. *> method (ii)
End method method1.
End static.
End class X.
Class Y.
Static.
Method-id. Main.
01 obj1 object.
01 obj2 type “B”.
01 obj3 type “A”.
Invoke type “X”::”Method1”(obj1) *> Uses method (i)
Invoke type “X”::”Method1”(obj2) *> Uses method (ii)
Invoke type “X”::”Method1”(obj3) *> also uses method (ii) since type B is ‘closer’
*> to type A than System.Object…
End method main.
End static.
End class Y.
Formally, the method overload that is ‘better’ than any other of the applicable method overloads is chosen. If no single overload is determined to be better than any other overload, then the match is considered to be ambiguous, and a compiler error results. See Better Method Overload.
Suppose we have an argument list A with a set of argument types (A1, A2,…An) and two applicable method overloads Mp and Mq with parameter types (P1…Pn) and (Q1,…Qn), where if necessary the parameters have been converted to expanded form (see above).
Then, Mp is said to be a better method overload than Mq if both the following are true:
Suppose we have two conversions, from type S to types T1 and T2, then the better conversion is determined as follows:
If the conversion from S to T1 is better than the conversion from S to T2, then it is also true that the conversion from S to T2 is worse than the conversion from S to T1.
An implicit conversion exists from type S to type T if it is possible to assign an object of type S to an object of type T (SET obj-T TO obj-S).
The following types of implicit conversion are available in the .NET system:
This simply means that S is the same type as T
COBOL generally allows any numeric item to be assigned to any other, with any loss of data being the responsibility of the user. However, when determining if a conversion exists for the purposes of method overloading, we distinguish between proper conversions and truncation conversions. Proper conversions are those for which no loss of data magnitude should result, though even in this case precision can be lost when converting between fixed point and floating point.
In the case of the .NET native types proper implicit conversions include:
The value 0 may be converted to any enum type
If S and T are reference types, S may be assigned to T when:
Any value type can be converted to a System.Object type by a process known as boxing. As part of this process the value is copied onto the object heap and a reference is created
Any numeric constant can be converted to any type capable of containing the full value of that constant. For instance the value 1 can be converted both to BINARY-CHAR UNSIGNED and to BINARY-CHAR.
A user defined implicit conversion consists of an optional implicit numeric conversion from S to S1 followed by the execution of a user defined implicit conversion operator resulting in T1, followed by another optional implicit numeric conversion to T. User defined implicit conversion operators are defined in COBOL with OPERATOR-ID IMPLICIT and result in methods with the name op_Implicit.
Copyright © 2009 Micro Focus (IP) Ltd. All rights reserved.