Data types

The data type of a data element defines what kind of data the data element can store. ABL supports the following basic kinds of data types:

ABL primitive types (see Table 1) 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).

You can use a primitive data type keyword in the following ABL syntax:

ABL object types (see Table 2) are complex types that function according to object-oriented principles. They can be broadly divided into two categories:

For information on the built-in ABL class, interface, and enumeration types, see the Class, Interface, and Enumeration Reference.

.NET primitive types (see Table 4) include data types that are built-into .NET languages. (Note that each language uses its own nomenclature. Compare, for example, the C# int versus 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.

ABL also references both the .NET primitive types and their corresponding mapped object types by mapping each ABL primitive type to a given set of .NET primitive and object type mappings (see Table 4). Thus, ABL documentation refers collectively to both the .NET primitive types and the corresponding .NET mapped object types as .NET mapped data types.

.NET object types (see Table 2) 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:

ABL also provides limited support for .NET abstract classes and .NET generic types. A .NET abstract class is similar to an ABL abstract class. A generic type has a type definition that can function as one of several different types, depending on type parameters used to complete the effective type name. .NET generic types are briefly described further in this entry. For more information on the basic kinds of .NET object types that ABL supports, see the notes section of this reference entry. OpenEdge also provides a set of built-in .NET class and interface types to support access to .NET object types. For information on the built-in .NET class and interface types, see the Class, Interface, and Enumeration Reference.

Within certain restrictions, an ABL class can inherit from a .NET class and implement .NET interfaces, similar to inheriting from an ABL class or implementing an ABL interface, respectively. When an ABL class inherits from a .NET class, any of its methods that override methods in the .NET class hierarchy can be called polymorphically on the .NET super class from both ABL and .NET. As a result, any ABL class that inherits from a .NET class becomes an ABL-derived .NET class. In fact, when an ABL-derived .NET class is instantiated in an ABL session, an instance with a corresponding .NET class type is also instantiated in the .NET context with reference to any ABL-overridden methods.

Similarly, when an ABL class implements a .NET interface, all of the ABL-implemented properties and methods can be called polymorphically on the interface type from both ABL and .NET. As a result, any ABL class that implements a .NET interface becomes an ABL-extended .NET class. In fact, when an ABL-extended .NET class that implements .NET interfaces is instantiated in an ABL session, an instance with a corresponding .NET class type is also instantiated in the .NET context with reference to the ABL-implemented properties and methods that might be accessed from .NET on each of the implemented interface types.

Note: An ABL-derived .NET class is also considered an ABL-extended .NET class. However, an ABL-extended .NET class that only implements .NET interfaces is not an ABL-derived .NET class. Thus, when OpenEdge documentation refers to an ABL-extended .NET class, it can also (but not necessarily) be referring to an ABL-derived .NET class.

ABL handle-based objects (see Table 2) 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 2) 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 4). For more information on support for both ABL arrays and .NET arrays, see the notes section of this reference entry.

The following table describes the primitive types supported in ABL.

ABL primitive types
Primitive type Description
BLOB BLOB (Binary Large OBject) specifies a database table or temp-table field that contains a BLOB locator, which points to the associated BLOB data stored in the database. You must use a MEMPTR to manipulate the binary contents of a BLOB field in ABL.
CHARACTER CHARACTER data consists of numbers, letters, and special characters.
CLOB CLOB (Character Large OBject) specifies a database table or temp-table field that contains a CLOB locator, which points to the associated CLOB data stored in the database. You must use a LONGCHAR to manipulate the character contents of a CLOB field in ABL.
COM-HANDLE A COM-HANDLE is a handle to a COM object (ActiveX Automation object or ActiveX Control).
DATE DATE fields contain dates.
DATETIME DATETIME data has two parts: an ABL date and an ABL time. The unit of time is milliseconds from midnight.
DATETIME-TZ DATETIME-TZ data has three parts: an ABL date, an ABL time, and an integer representing the time zone offset from Coordinated Universal Time (UTC). The unit of time is milliseconds from midnight. The unit of time zone offset is minutes.
DECIMAL DECIMAL data consists of decimal numbers up to 50 digits in length including up to 10 digits to the right of the decimal point.
HANDLE A HANDLE is a pointer to an ABL handle-based object. This can be a compile-time defined static object or a run-time defined dynamic object.
Note: HANDLE and WIDGET-HANDLE can be assigned to each other and used interchangeably. WIDGET-HANDLE is supported only for backward compatibility.
INT64 An INT64 consists of 64-bit data (whole numbers).
INTEGER An INTEGER consists of 32-bit data (whole numbers).
LOGICAL LOGICAL data evaluates to TRUE or FALSE (or YES or NO).
LONGCHAR A LONGCHAR consists of CHARACTER data that is not limited to 32K in size. You can use a LONGCHAR to manipulate the character contents of a CLOB database or temp-table field in ABL.
MEMPTR A MEMPTR contains a sequence of bytes in memory. You can use a MEMPTR to manipulate the contents of a BLOB database or temp-table field in ABL.
RAW RAW data can be any kind of data, even data from non-OpenEdge databases. It is not converted in any way.
RECID A RECID is a unique internal identifier for a record within a single database storage area.
Note: RECID is supported mainly for backward compatibility. For most applications, use ROWID instead.
ROWID A ROWID is a unique internal identifier for a record within a single database storage area. For partitioned tables, it is also unique across all partitions of a given table.
WIDGET-HANDLE A WIDGET-HANDLE is a pointer to an ABL handle-based object.
Note: HANDLE and WIDGET-HANDLE can be assigned to each other and used interchangeably. WIDGET-HANDLE is supported only for backward compatibility.

The following table describes the non-primitive (complex) types supported in ABL.

ABL complex types
Complex type Description
Array 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.

In ABL, you must also enclose any .NET array object type name in double-quotes in order to handle the square brackets and any commas, which are special characters in ABL names, for example, "System.Drawing.Point[]".

For more information on specifying array types, see the Type-name syntax reference entry.

Class Classes encapsulate a set of data and behavioral elements (members), including implementations for data members, properties, and methods. Class definitions can also implement interfaces, which define a common set of prototypes for methods, properties, and events that classes can implement.

For more information on classes and interfaces, see OpenEdge Development: Object-Oriented Programming.

Enumeration An enumeration is made up of a list of strongly typed, named constants called members. The value of a variable defined as an enumeration type is restricted to the list of members defined for that enumeration type.
Handle-based object A handle-based object has a built-in and inherent ABL type that provides data and behavior of varying complexity depending on the purpose of the object. A few examples include:
Note: While each type of handle-based object is unique, because of their weak typing, you can reference all such objects using the same primitive type, HANDLE (see Table 1).

For more information on each type of handle-based object, see the reference entry for its type in the Widget Reference or the Handle Reference, and see the reference entry for its respective DEFINE, CREATE, or other instantiating executable statement.

The following table lists the default data formats and initial values for ABL primitive and object types.

Default ABL data type initial values and display formats
Data type Default initial value Default display format
BLOB1, 3 Unknown value (?) See footnote 1.
CHARACTER "" (an empty string) X(8)
CLASS2, 3, Unknown value (?) See footnote 1 .
CLOB1, 3 Unknown value (?) See footnote 1.
COM-HANDLE3 Unknown value (?) >>>>>>9
DATE Unknown value (?) (displays as blanks) 99/99/99
DATETIME Unknown value (?) 99/99/9999 HH:MM:SS.SSS
DATETIME-TZ Unknown value (?) 99/99/9999 HH:MM:SS.SSS+HH:MM
DECIMAL 0 ->>,>>9.99
HANDLE3 Unknown value (?) >>>>>>9
INT64 0 ->,>>>,>>9
INTEGER 0 ->,>>>,>>9
LOGICAL no yes/no
LONGCHAR1 "" (an empty string) See footnote 1.
MEMPTR1, 3 A zero-length sequence of bytes See footnote 1.
RAW1, 3 A zero-length sequence of bytes See footnote 1.
RECID Unknown value (?) >>>>>>9
ROWID1, 3 Unknown value (?) See footnote 1.

For more information on using the built-in ABL primitive types, see OpenEdge Getting Started: ABL Essentials and the Web paper, ABL Data Types in OpenEdge Release 10.

As noted previously in this entry, ABL supports references to .NET types in two basic ways:

  1. You can make direct and explicit reference to .NET object types using similar syntax that is supported for referencing ABL user-defined class and interface types. For supported .NET object types, this includes both the instantiation of a .NET class in ABL and the derivation of the .NET class by an ABL user-defined class (ABL-derived .NET class), and it includes the implementation of supported .NET interfaces by an ABL user-defined class (ABL-extended .NET class). To integrate the .NET class hierarchy with the ABL class hierarchy, ABL views System.Object as an immediate subclass of the ABL root class (Progress.Lang.Object class). In this way, you can manage .NET object types in ABL using many of the same mechanisms that you use for managing ABL class and interface types. However, you must observe the following limitations:
    • You cannot directly reference any .NET object type that is supported as a .NET mapped data type, except to define a .NET array of such types. You can only reference .NET mapped data types as their equivalent ABL built-in primitive types. For more information on .NET mapped data types, see the immediately following Step 2.
    • 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.
    • You cannot define an ABL interface that inherits from a .NET interface.
    • ABL imposes additional requirements on the .NET classes you can extend and the .NET interfaces you can implement in an ABL user-defined class. For more information, see the CLASS statement and INTERFACE statement reference entries.

    For more information on the requirements for accessing .NET object types, see the notes section of this reference entry and OpenEdge Development: GUI for .NET Programming.

  2. You can make implicit access to all .NET primitive types and their associated mapped object types by using the ABL built-in primitive types that are mapped to them. Because .NET mapped object types and .NET primitive types, together, represent the complete set of .NET mapped data types, the implicit mapping between .NET mapped data types and ABL primitive types allows you to access .NET method parameters, fields (data members), and properties using the corresponding ABL primitive types without direct reference to their .NET data type equivalents. In fact, ABL does not allow you to directly reference either the .NET primitive types or the .NET mapped object types as scalars without raising a compile-time error. (The exception is when defining a .NET array of mapped types. For more information, see the notes section of this reference entry.)

The following table shows the implicit mappings supported between .NET mapped data types and ABL built-in primitive types, showing the corresponding primitive types from C#.

Implicit mappings between .NET and ABL data types
Implicit .NET object type Implicit C# primitive type ABL primitive type
System.Boolean bool LOGICAL
System.Byte byte INTEGER4, 5
System.SByte sbyte INTEGER4
System.DateTime N/A DATETIME
System.Decimal decimal DECIMAL6, 7,
System.Int16 short INTEGER4
System.UInt16 ushort INTEGER4, 5
System.Int32 int INTEGER7
System.UInt32 uint INT648, 5
System.Int64 long INT647
System.UInt64 ulong DECIMAL5, 9
System.Double double DECIMAL10
System.Single float DECIMAL10
System.Char char CHARACTER11
System.String string CHARACTER7 or LONGCHAR7, 12

Thus, instead of using an object reference to the corresponding .NET mapped object type, you must provide or access all .NET primitive (or mapped object type) values for .NET methods, data members, and properties as ABL primitive types. Similarly, when you reference any data element or value defined as a .NET mapped data type, ABL evaluates the .NET value to its corresponding ABL primitive value. ABL checks for .NET/ABL type compatibility at compile time, except in rare cases where data type narrowing is allowed, in which case the AVM checks for data overflow or underflow at run time.

Note: To access all other .NET value types in ABL except mapped data types (for example, System.Drawing.Size), you can and must use object references to the value type objects.

.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.

So, in addition to implicitly mapping its native primitive types to their corresponding .NET mapped data types, ABL also supports boxing between its primitive or array types and a .NET System.Object or array object. ABL performs boxing operations automatically in two cases:

However, as described further in this entry, ABL does not support automatic boxing operations when passing parameters to ABL routines.

When ABL does automatic boxing that involves ABL primitive types or arrays of elements containing primitive types, it also does implicit conversion between these types and the corresponding .NET mapped types (see Table 4). For example, if you assign an ABL INTEGER to a System.Object, ABL converts the ABL INTEGER to a System.Int32, which the System.Object accepts as a subclass value. Similarly, if you assign an ABL INTEGER array to a System.Object, ABL converts the ABL INTEGER array to a "System.Int32[]", which the System.Object accepts as a subclass value. The same occurs when you pass an ABL INTEGER or INTEGER array to a System.Object INPUT parameter of a .NET method.

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.

In a similar manner, ABL also does automatic boxing directly between compatible ABL arrays and one-dimensional .NET array objects. For example, if you assign or pass .NET method parameters between a "System.Windows.Forms.Button[]" array object and an ABL array of System.Windows.Forms.Button elements, ABL automatically does the required boxing and unboxing to convert between the different array types. A similar boxing and unboxing operation occurs between an ABL primitive array and a compatible .NET array of mapped types, for example, between an ABL array of INTEGER and a .NET "System.Int16[]" array object. For more information on boxing and unboxing between ABL and .NET arrays, see the notes section in this reference entry on working with .NET arrays.

However in the following four ABL contexts, automatic ABL boxing or unboxing is either not supported or might not be supported as 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 4, 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 .NETdefault match for the ABL primitive type (indicated by a footnote 7 in Table 4). 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.

Similarly, three additional cases exist (other than the need for explicit boxing) where you must specify the .NET data type mapping you want for a given ABL primitive type:

To indicate a non-default .NET mapped type in the previous cases where you want an explicit mapped type to be used, the syntax for the following ABL elements allows you to specify an appropriate ABL keyword:

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 5 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.

Note: The AS data types in Table 5 represent some different data types than you can specify using the AS option to pass a COM method parameter. For more information on passing COM method parameters, see Syntax for accessing COM object properties and methods.
Explicit mappings between ABL and .NET data types
Explicit .NET object type Explicit C# primitive type ABL primitive type ABL AS data type
System.Boolean bool LOGICAL13
System.Byte byte INTEGER UNSIGNED-BYTE14
System.SByte sbyte INTEGER BYTE
System.DateTime N/A DATETIME13
System.Decimal decimal DECIMAL13
System.Int16 short INTEGER SHORT
System.UInt16 ushort INTEGER UNSIGNED-SHORT
System.Int32 int INTEGER13
System.UInt32 uint INT64 UNSIGNED-INTEGER
System.Int64 long INT6413
System.UInt64 ulong DECIMAL UNSIGNED-INT64
System.Double double DECIMAL DOUBLE
System.Single float DECIMAL FLOAT
System.Char char CHARACTER SINGLE-CHARACTER
System.String string CHARACTER/ LONGCHAR13

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:

System.Collections.Generic.List<T>

In ABL, you can only reference a .NET generic type as a constructed type using a type name that has the following syntax:

"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 5.

For example, to define an object reference to a System.Collections.Generic.List<T> that is constructed as a list of System.Int16, you might use the following ABL statement:

DEFINE VARIABLE shortList AS 
  CLASS System.Collections.Generic.List<SHORT> NO-UNDO.

You can also reference an array of a generic type and define generic types with a type parameter that is an array. For more information, see the information on .NET arrays in the notes of this reference entry.

You can use a .NET generic type in all the same contexts as any other .NET type except to define an ABL class that:

Also, while you can cast an object reference to a .NET generic type using the CAST function, you cannot cast to a .NET generic type using the DYNAMIC-CAST function.

For more information on how to identify .NET generic types and understand the constraints on their type parameters, see the .NET documentation on MSDN. For more information on working with .NET generic types in ABL, see OpenEdge Development: GUI for .NET Programming.

Notes

See also

Assignment (=) statement, ASSIGN statement, BOX function, CLASS statement, INTERFACE statement, NEW function (classes), Progress.Data.BindingSource class, Type-name syntax, Progress.Util.TypeHelper class, UNBOX function