ABL primitive types (see
Table 21) are built-in data types that can hold values with relatively simple content and that support relatively simple operations that can typically be applied using built-in ABL operands, such as arithmetic or relational operands (for example, the
+ Addition operator, or the
EQ or = operator). The values for all ABL primitive types, other than the MEMPTR, BLOB, CLOB, and LONGCHAR, are limited in memory size to 32KB. MEMPTR and LONGCHAR variables can be any size, and BLOB and CLOB fields can be up to 1GB in size. Note that you can define BLOB and CLOB data types only for table or temp-table fields, and, unlike most other ABL primitive types, the operations they support do not have built-in ABL operands, but are available using built-in ABL functions (for example, the
COPY-LOB statement).
ABL object types (see
Table 22) are complex types that include class and interface types, and which function according to object-oriented principles. ABL supports a set of built-in object types and also allows you to create your own user-defined object types using the
CLASS statement and
INTERFACE statement. All ABL class types ultimately inherit from the ABL root class,
Progress.Lang.Object class. Each object type encapsulates a set of data and behavioral elements (
members). ABL class members can include implementations for data members, properties, and methods, including instance members that are available for access on any instance of that class and static members that are available for access using the class type itself, regardless if a class instance of the type exists. An interface type can define a common set of prototypes for methods, properties, and events that classes can implement. An ABL class can also be defined as abstract, which allows it to optionally define certain members as abstract. Abstract class members can be methods, properties, or events that are similar to interface member prototypes, but they can only be implemented by a class that derives from the abstract class. Aside from defining and implementing its own members, an ABL class can inherit members from another class and it must implement all member prototypes from interfaces that it implements and all abstract members from any abstract class that it inherits.
You can create instances of classes (class-based objects) at run time using the
NEW function (classes), and you can reference each instance and its PUBLIC members using an
object reference, which is an ABL data element defined to reference a specific object type. You can define an object reference for any kind of ABL data element that you can define as an ABL primitive type (except a database table field). However, you can define a field of a temp-table as an object reference to the ABL root class (
Progress.Lang.Object class).
|
A class type (subclass) that is derived from the class type of the referenced class instance (if and only if the referenced class instance is an instance of the derived class type)
|
.NET primitive types (see
Table 24) include data types that are built-into .NET languages that each language names in its own way (for example, the C# int and the Visual Basic Integer). .NET also aliases (
maps) a standard set of object types (
.NET mapped object types, for example,
System.Int32) to these primitive types. All .NET languages can reference each such type as either the primitive type that the particular language supports or as the alias for the corresponding .NET object type that every language supports.
.NET object types (see
Table 22) include all class types (and their derivatives) that derive from the .NET root class,
System.Object, and .NET interface types, which can inherit from other .NET interface types, but otherwise function for a class much like an ABL interface type. You can reference a .NET object type like an ABL object type, by using an ABL object reference defined as that object type, or by referencing members of a .NET class. .NET object types also consist of two basic kinds of types:
|
Value types — Objects that .NET creates, passes, and assigns by value. Value type objects all inherit from the .NET class, System.ValueType. In ABL, when you access a value type from .NET, you access a new copy of the object that is separate from the one that is maintained by .NET. If you then change object data in ABL, these changes do not appear in any copy of the object maintained by .NET. In addition, the ABL object reference to the ABL copy of the object has no affect on the .NET garbage collection of any .NET copy of the object.
|
|
Reference types — Objects that .NET creates, passes, and assigns by reference. In ABL, when you access a .NET reference type, you access the same copy of the object that is maintained by .NET. If you then change object data in ABL, these changes also appear in .NET, because .NET references the same object. In addition, the ABL object reference is counted as a reference to the object for .NET garbage collection.
|
ABL handle-based objects (see
Table 22) include a set of complex, weakly-typed objects, some of which ABL provides as built-in system objects (such as, the
SESSION system handle), and others that ABL supports as a pre-defined set of objects that you can create as needed (such as, a
FRAME widget or a record buffer). Handle-based objects exist independently and have no inheritance hierarchy like class-based objects. However, like class-based objects, handle-based objects have members consisting of a set of attributes (data) and methods (behavior). Depending on the object, you can create a handle-based object as a compile-time (static) object using an appropriate DEFINE statement or as a run-time (dynamic) object using an appropriate CREATE statement or other executable statement. You can reference system objects using the built-in system handle (keyword) pre-defined for them. You can reference static handle-based objects by name, using appropriate syntax for each type, and you can reference dynamic or static handle-based objects using a common primitive data element known as a handle, which you define as the HANDLE data type. Because of the weak typing of these objects, you can reference all static and dynamic handle-based objects that you define or create using the same handle. ABL also provides some system handles that provide access to particular types of pre-defined handle-based objects that are in a given state (such as, the
CURRENT-WINDOW system handle for accessing a particular
WINDOW widget).
ABL arrays (see
Table 22) are limited to one dimension and can include elements of any primitive or object type that you can define for a variable (see the
DEFINE VARIABLE statement reference entry). ABL also provides support for mapping ABL arrays to one-dimensional .NET array objects of the same element type. This means that while you can access all .NET arrays as class instances, you can also make direct array assignments and pass routine parameters between ABL arrays and equivalent one-dimensional .NET array objects. This also includes .NET arrays whose elements are .NET mapped data types (referred to as
.NET arrays of mapped types), where assignments to or from ABL arrays work using the rules of implicit data type mapping. The element types supported for a .NET array of mapped types are identical to the .NET data types that ABL implicitly maps to ABL primitive types (see
Table 24). For more information on support for both ABL arrays and .NET arrays, see the notes section of this reference entry.
Table 21 describes the primitive types supported in ABL.
Table 22 describes the non-primitive (
complex) types supported in ABL.
|
|
|
An ABL array type is complex type that specifies a one-dimensional array of elements of the same scalar data type with a 1-based index, where a scalar data type is any data type that is not, itself, an array. The elements of an ABL array can contain scalars of any supported ABL primitive type, any ABL object type, or any .NET object type (other than a .NET mapped object type). The type definition for an ABL array is specified by the type definition for the array element with the addition of the EXTENT option (or with the Extent option selected in OpenEdge database tools). Thus, you can define an ABL array data element using similar features and syntax used to define primitive and object-type data elements. However, you cannot define ABL array types for the BLOB or CLOB primitive type.
Also, note that unlike ABL arrays, .NET arrays are objects with an object type, just like any other .NET type. However, all .NET array object types derive from the System.Array class (a reference type), and you can create and access .NET arrays using public members of System.Array. Also, .NET arrays can be multi-dimensional and typically have a 0-based index. In .NET, the object type name of an array object is the object type name of its array elements appended with a set of square brackets ( []) with an embedded comma added for each additional dimension in the array. For example, System.Drawing.Point[] is a one-dimensional array object type and System.Drawing.Point[,] is a two-dimensional array object type. .NET languages support syntax for additional kinds of array objects, including jagged arrays. For more information on .NET array syntax, see the “Arrays Tutorial” in the C# Programmer’s Reference on MSDN.
|
[ CLASS ] object-type-name
|
Specifies an object type, where object-type-name can specify any ABL or .NET object type name. (CLASS is an optional keyword that can be used in ABL syntax to define an object reference to the specified object type.) You can specify an object-type-name in order to:
|
|
|
Table 23 lists the default data formats and initial values for ABL primitive and object types.
|
You cannot use System.Threading.Thread, or any derived class—ABL is single-threaded.
|
|
You cannot use System.MulticastDelegate, or any derived class (otherwise referred to as delegates) to provide handlers for .NET events. ABL provides its own event handling model for .NET events. For more information, see the “Class Events Reference” section.
|
Table 24 shows the implicit mappings supported between .NET mapped data types and ABL built-in primitive types, showing the corresponding primitive types from C#.
.NET supports a concept known as boxing. Boxing is the process of converting a value type (such as a C# int or .NET System.Int32) to a reference type object. Boxing a value type wraps its value inside a
System.Object.
Unboxing extracts the value from the
System.Object as the original value type. In .NET, boxing and unboxing between a value type and a
System.Object occurs during assignment or parameter passing.
In reverse, when you assign an appropriate System.Object to an ABL INTEGER or INTEGER array, ABL unboxes the
System.Object by determining the .NET mapped type that the
System.Object represents, converts that value to its equivalent ABL primitive or primitive array value, and attempts to assign the result to the ABL INTEGER or INTEGER array (which is validated at run time). For example, if the
System.Object represents the
System.Decimal subclass and you are assigning it to an ABL INTEGER, ABL converts the
System.Decimal value to an ABL DECIMAL and attempts to assign it to the ABL INTEGER.
|
When you use a System.Object directly in an expression, ABL does not unbox the System.Object into a compatible ABL primitive type.
|
|
When you assign an ABL primitive value (or primitive array) to a System.Object and the ABL primitive type maps to multiple .NET data types, it automatically boxes the ABL primitive value (or primitive array elements) as the default matching .NET mapped object type, which might not be the .NET data type mapping that you require.
|
When you use a System.Object directly in an expression, ABL raises a compile-time error because ABL does not support automatic unboxing of a
System.Object in an expression. Instead, you can use the ABL built-in
UNBOX function in the expression to explicitly unbox the value. This function accepts the
System.Object as input and returns an ABL primitive value that is equivalent to the .NET mapped object type value (subclass) represented by the specified .NET
System.Object instance.
When you assign an ABL primitive value or primitive array to a System.Object, ABL always boxes the value or array into a particular .NET mapped type or array of mapped types, which might not be the .NET type you want. In
Table 24, several ABL primitive types implicitly map to more than one .NET mapped data type. For each ABL primitive type that maps to multiple .NET data types, ABL uses one of these mappings as the
.NET default match for the ABL primitive type (indicated by a footnote
4 in
Table 24). Thus, when you assign an ABL primitive value or primitive array to a
System.Object, ABL automatically boxes the value or array using the .NET default match for the specified ABL data type. For example, by default an INTEGER automatically boxes as a
System.Int32, and an INTEGER array automatically boxes as a
"System.Int32[]".
However, you can explicitly box the ABL value or array using a .NET mapped type other than the default match with the ABL built-in BOX function. This function accepts an ABL primitive value or array as input and, by default, returns a boxed .NET type according to the .NET default match for the ABL data type of the input value or array. In order to box the value using a mapped type other than the .NET default match, you can pass an ABL keyword as a string to the function that indicates the explicit .NET type you want to use. For example, an ABL DECIMAL value can represent both a .NET
System.Decimal (the default match) and a
System.Double (among other possible types). If you need to box the ABL DECIMAL as a .NET
System.Double, you can explicitly indicate this to the BOX function. Similarly, if you need to box an ABL DECIMAL array as a
"System.Double[]", you can use the same indication.
Note:
|
When you unbox a System.Object using the UNBOX function, you cannot similarly specify a particular ABL primitive or primitive array type as the result. ABL always unboxes any System.Object using the default matching ABL type.
|
Another case for which you must use the BOX function or the
UNBOX function is when you pass parameters between compatible ABL primitive or array types and .NET object types in the parameters of ABL methods, procedures, and user-defined functions. ABL raises a compiler error if you try to pass these types to each other directly in ABL routine parameters. Appropriate use of the BOX function or UNBOX function allows this type of parameter passing to occur without a compile-time error. Note, again, that ABL
does support the automatic boxing and unboxing of .NET objects in parameter passing for .NET method calls.
|
When you pass an ABL primitive value (not an array) to a .NET method or constructor parameter that is a System.Object, and you want the result to be a .NET mapped type other than the default match. You must indicate the explicit mapped type on the passed ABL primitive argument. A common use case is the SetValue( ) method of the System.Array class, which sets the value of a .NET array element. If the .NET type of the array element is other than the default match, you must indicate the .NET mapped type for the value parameter to match the array definition.
|
This keyword is referred to as an AS data type, because you specify it for a passed parameter using the AS option. So, for example, when you override a .NET method, you must explicitly specify .NET data type for each .NET mapped parameter, property, or return type. If the .NET data type is a default match, you must simply use the matching ABL data type. Otherwise, you must indicate the appropriate AS data type keyword for the data type of the method parameter, return type, or property definition.
Table 25 lists each explicit .NET data type mapping for a given ABL primitive type. For each listed .NET data type, you indicate this explicit mapping either by using the corresponding ABL primitive type (for a default match) or by using the appropriate option to specify the AS data type that corresponds to the explicit .NET data type you want to map. For more information on specifying the AS data type option when using the BOX function or when calling overloaded .NET methods, see the reference entries for the
BOX function and
Parameter passing syntax in this book. For more information on specifying AS data types when overriding a .NET method, or when implementing (or overriding) a .NET interface (or abstract) method, property, or event, see the
METHOD statement, the
DEFINE PROPERTY statement, the
DEFINE EVENT statement, or the
Parameter definition syntax reference entry, as appropriate.
A .NET generic type is a class or interface defined so that it functions as one of several different types, depending on how you reference its type name. A reference to a .NET generic type name includes one or more type parameters, each of which specifies a data type that the generic type can use in its implementation. When you reference the generic type name in ABL, you substitute a specific data type for each type parameter defined for the generic type. This reference then identifies the generic type as a
constructed type. The notation for a generic type that you see in .NET documentation or in a class browser, where the type parameters are not resolved, is called an
open type. An open type reference contains only placeholders for the parameters in the type name, such as
<T>, which defines the single parameter for the following generic type:
"namespace.object-name<type-parameter [ , type-parameter ] ...>"
|
The namespace is a .NET namespace and the syntax from
object-name up to and including the right angle bracket (
>) forms the
dotNET-object-name as described in the
Type-name syntax reference entry. The left and right angle brackets (
<>) are the part of .NET generic type name references that enclose the type parameters, as shown in the previous example. Each
type-parameter in the parameter list represents a placeholder for a specific .NET data type. The number of type parameters and the data type that you can specify for each
type-parameter in a constructed type reference depends on the generic type definition. However, you can never specify an ABL object type or an ABL-extended .NET class type as the data type of any
type-parameter; it can only be a pure .NET type. The quotes are required in order to allow for the angle brackets and any spaces in the type name.
The definition for each type-parameter in a .NET generic type definition can specify constraints that determine the .NET types you can substitute for a given parameter when you reference the constructed type. If these constraints on a
type-parameter allow you to specify one or more .NET mapped types, you must specify an appropriate explicit mapping for each such type when you specify the
type-parameter in ABL, as described in
Table 25.
|
Wherever integer appears, this is a reference to the INTEGER or INT64 data type.
|
|
Wherever character appears, this is a reference to the CHARACTER, LONGCHAR, or CLOB data type.
|
|
Wherever decimal appears, this is a reference to the DECIMAL data type.
|
|
Wherever numeric appears, this is a reference to the INTEGER, INT64, or DECIMAL data type.
|
|
Starting with Release 10.1B, all intermediate calculations are carried out in 64-bit arithmetic. For example, 2,000,000,000 * 100 / 100 gives the correct result whether the target field is INTEGER or INT64. However, although 2,000,000,000 * 100 does not cause an overflow, you must assign the result to an INT64 field. If you assign it to an INTEGER field, the AVM generates a run-time error.
|
The variable someIntArray is now defined as an array of four integers. Since the size is fixed at 4, this is a
determinate array. You can also define an
indeterminate array by omitting the constant integer value after EXTENT. In this case, the number of elements in the array is undefined.
Here, each element of the anotherIntArray will be copied into the corresponding element of the
someIntArray. This is called a
deep copy. Note that unsubscripted array references are not supported in expressions or comparison operations. For more information on array assignments, see the
Assignment (=) statement reference entry.
|
Classes — Viewed and managed like ABL classes with support for additional features that are unique to .NET classes, such as inner classes and indexers for indexed properties. For information on accessing instances of .NET classes, see the Class-based object reference entry.
|
|
Interfaces — Viewed and managed as ABL interfaces with support for additional features that are unique to .NET, such as inner interfaces.
|
|
Structures — Viewed and managed similar to ABL classes. Structure types are supported using syntax native to each .NET language, for example, using the struct keyword in C# and C++. For information on accessing instances of .NET structures, see the Class-based object reference entry. The essential difference between .NET structures and most other .NET classes is that structures inherit from System.ValueType and are therefore value types. Thus, all structures are passed within .NET, and between ABL and .NET, by value. However within ABL, structure objects are passed, like all other class instances, by reference. Therefore, when you access a structure from .NET, you reference a copy of the object in ABL that is separate from the object in .NET, and when you pass an ABL reference to a structure back to .NET, .NET gets a copy of the object that is separate from the object that is referenced in ABL. Structures therefore have different object management requirements in ABL than reference type objects. For more information, see the information on ABL support for value types in OpenEdge Development: GUI for .NET Programming.
|
|
Enumerations — Unique to .NET, enumerations are classes that correspond to a named set of constant values with a single underlying data type. Each of these constant values corresponds to a member of a given enumeration class. Each .NET language allows you to define and reference enumeration members using its own syntax. ABL also provides syntax that allows you to reference .NET enumerations as object types. For more information, see the Enumeration member access reference entry. Enumerations inherit from the System.Enum structure, which inherits from System.ValueType. Thus, like structures, enumerations are value types that are passed by value between .NET and ABL. However, unlike .NET languages that can view enumerations as values, ABL views enumerations only as objects that are passed by reference, like any other class instance. Enumerations therefore have similar object management requirements to structures in ABL. For more information, see the information on ABL support for value types in OpenEdge Development: GUI for .NET Programming.
|
|
A .NET array is an object that extends the System.Array class. You can access a .NET array in ABL using an object reference, like any other object. Thus, in ABL, you can access all .NET arrays, of all dimensions, whose element type is either an ABL-supported .NET object type (such as System.Windows.Forms.Form) or a .NET mapped data type (such as System.Int16 or C# short). You can also create .NET arrays directly in ABL by creating instances of the System.Array class. Note that while you cannot explicitly define a variable as a System.Int16, ABL does allow you to define a .NET array where the element type is a System.Int16 (or any other mapped object type). .NET arrays have the following class hierarchy in ABL, in order of derivation from the ABL root class:
|
Because all .NET array objects inherit from System.Array, you can access any .NET array object using the members of the
System.Array class. To help create .NET array objects, OpenEdge provides a
Progress.Util.TypeHelper class to specify the
System.Type object needed for creating .NET array objects. For more information on working with .NET arrays, see the sections on accessing .NET arrays in
OpenEdge Development: GUI for .NET Programming.
|
An array assignment can occur between .NET and ABL arrays of compatible element types, either by direct assignment of one array to another using the Assignment (=) statement or the ASSIGN statement, or by passing array parameters to .NET methods using Parameter passing syntax. In these specific cases, ABL performs automatic boxing and unboxing between the compatible ABL and .NET array types. In general, you can assign an ABL or .NET array of .NET value types (such as System.Drawing.Size) only to another ABL or .NET array of identical value type elements. ABL makes an exception if the ABL array in the assignment is an array of primitive type elements (such as INTEGER), in which case ABL allows assignment to or from a compatible .NET array of mapped types (such as "System.Int32[]" or "System.Byte[]"). Otherwise (for reference types), elements of the target array must be identical to or higher in the class hierarchy than the elements of the source array. For example, you can assign an array of System.Windows.Forms.Form elements to an array of System.Object elements.
|
|
If the target is an ABL array of System.Object elements and the source array is an array of .NET mapped types (such as System.Int32) or ABL primitive types, ABL raises a compile-time error, because (as noted previously) ABL does not automatically do the unboxing and boxing operations that are required on the elements of each array.
|
|
If the target is a System.Array or a System.Object, you can assign to it any .NET source array object or any ABL source array that is defined as a supported .NET object type or as an ABL primitive type that maps implicitly to a .NET mapped data type (see Table 24). If the source is a .NET array object, ABL simply assigns it to the System.Array or System.Object reference. If the source is an ABL array, ABL creates a new .NET array object to hold the ABL array elements and assigns it to the System.Array or System.Object reference. In addition, if the ABL array elements have a primitive type, ABL automatically maps the ABL array elements into the default matching .NET object type before storing them in the specified System.Array or System.Object.
|
|
If the target is a .NET array of System.Object elements ( "System.Object[]"), you can also assign any compatible ABL or .NET array to it. Note (as previously described) that a source array with .NET value type elements (such as System.Drawing.Size or ABL INTEGER, which resolves to the value-type, System.Int32) is not compatible. .NET requires that the element types must be identical in array assignments involving value type elements.
|
|
If the target is a Progress.Lang.Object, you can assign to it any .NET source array object, but not a native ABL array (which is not an object).
|
|
You can pass the Unknown value (?) as a parameter to a .NET method or assign the Unknown value ( ?) to a .NET property or data member. ABL translates the Unknown value ( ?) in these cases to the .NET null value. For the numeric and logical .NET primitive types listed in Table 24, when they are set to null, .NET sets a different default value—0, 0.0, or no—depending on the data type. In these cases, ABL also returns the .NET null value as the ABL Unknown value ( ?).
|
|
ABL does not do any mapping between System.Data.DataSet or System.Data.DataTable method parameters, properties, or data members on one hand and ABL ProDataSets and temp-tables on the other. ABL supports data binding between ProDataSets or temp-tables (among other data sources) and .NET form controls using the Progress.Data.BindingSource class (the ProBindingSource). For more information, see the Progress.Data.BindingSource class reference entry. However, you can always directly access .NET DataSet and DataTable objects as any other .NET object, using their class members.
|