Defines a property of a class, declares a property prototype in an ABL interface, or overrides an abstract property inherited from an ABL or .NET abstract super class. A property is a named member of a class that allows you to read or write a value using the same syntax as a data member. However, a property can define special methods (
accessors) that specify if and how the property can be read or written. The following description begins with the syntax for defining a property that you can both read and write.
DEFINE [ PRIVATE | PROTECTED | PUBLIC ] [ STATIC | ABSTRACT ] [ OVERRIDE ]
PROPERTY property-name
{ {
AS primitive-type-name |
AS [ CLASS ] object-type-name
} [ EXTENT [ constant ] ] }
[ INITIAL { constant | { [ constant [ , constant ] ... ] } } ]
[ NO-UNDO ]
{
[ accessor-access-mode ] GET [ implementation ] .
SET [ implementation ] .
|
GET [ implementation ] .
[ accessor-access-mode ] SET [ implementation ] .
}
|
DEFINE [ PRIVATE | PROTECTED | PUBLIC ] [ STATIC | ABSTRACT ] [ OVERRIDE ]
PROPERTY property-name
{ {
AS primitive-type-name |
AS [ CLASS ] object-type-name
} [ EXTENT [ constant ] ] }
[ INITIAL { constant | { [ constant [ , constant ] ... ] } } ]
[ NO-UNDO ]
GET [ implementation ] .
|
DEFINE [ PRIVATE | PROTECTED | PUBLIC ] [ STATIC | ABSTRACT ] [ OVERRIDE ]
PROPERTY property-name
{ {
AS primitive-type-name |
AS [ CLASS ] object-type-name
} [ EXTENT [ constant ] ] }
[ INITIAL { constant | { [ constant [ , constant ] ... ] } } ]
[ NO-UNDO ]
SET [ implementation ] .
|
DEFINE [ PUBLIC ] PROPERTY property-name
{ { AS primitive-type-name | AS [ CLASS ] object-type-name }
[ EXTENT [ constant ] ] [ NO-UNDO ] }
{ GET.
SET.
|
GET.
|
SET. }
|
DEFINE [ PROTECTED | PUBLIC ] [ OVERRIDE ] ABSTRACT PROPERTY property-name
{ { AS primitive-type-name | AS [ CLASS ] object-type-name }
[ EXTENT [ constant ] ] [ NO-UNDO ] }
{ GET.
SET.
|
GET.
|
SET. }
|
[ PRIVATE
| PROTECTED
| PUBLIC
]
Without this option, ABL defines an instance property that is scoped to a single instance of the class where it is defined. ABL creates one copy of the specified instance property for each such class instance that you create. You can reference any public instance property (abstract or non-abstract) in any procedure, or in any instance or static method defined inside or outside of the class where the instance property is defined. Any static method can reference the public instance property only using an object reference to a class instance that defines the property as a member. If the referencing static method is defined in the same class as the public instance property, the class must instantiate itself in order to have access to an instance reference.
Note:
|
You cannot use a class instance that is not equal to the THIS-OBJECT system reference to reference a private or protected instance property that is defined in the same class, because PRIVATE and PROTECTED access modes are instance based in ABL. Thus, private and protected instance members are accessible only to other members of the same class instance, where as public instance members can be accessed from other instances of the same class, including the session “static instance” of the class
|
When you specify OVERRIDE, the property definition must match the inherited abstract property with respect to name, data type (including any EXTENT), specified accessors (GET or SET), and NO-UNDO setting. In addition, the access mode must not be more restrictive than the access mode defined for the overridden property. When overriding a .NET abstract property, the data type must also map appropriately to the inherited .NET property data type. For more information, see the description of the
primitive-type-name and
object-type-name options.
If you specify the ABSTRACT option, your overriding property is also defined as abstract, and it must be implemented in a class derived from the defining class. Note that you do not have to override an inherited abstract property that you want to remain abstract as long as the inheriting class is also abstract. However, doing so allows you to specify a less restrictive access mode for the abstract property.
Specifies the name of the property. The property-name must be unique among the names of all properties, events, and variable data members that are defined in the class and its inherited class hierarchy, and that are accessible to the defining class (not defined as PRIVATE in a super class).
Note:
|
Members of a class are grouped into six namespaces, including buffers/temp-tables, methods, variables/properties/events, ProDataSets, queries, and data-sources. Variables, properties, and events defined as members of a class share the same namespace. There can be only one class member in this namespace with a given name (not counting abstract member overrides).
|
Specifies a built-in primitive type (primitive-type-name) and an optional initial value (
constant) for the default memory of the property. The built-in data type can be one of the following:
CHARACTER | COM-HANDLE | DATE | DATETIME | DATETIME-TZ | DECIMAL
| HANDLE | INT64 | INTEGER| LOGICAL | LONGCHAR | MEMPTR | RAW | RECID | ROWID | AS-data-type
|
If you are overriding a .NET abstract property or implementing a property defined in a .NET interface,
primitive-type-name must specify the exact same .NET mapped data type that is defined for the corresponding .NET property. For a .NET mapped data type that is a default match for a given ABL primitive type, you must use the default matching ABL data type, as shown in
Table 24. (For example, INTEGER indicates a .NET System.Int32.) For a .NET mapped data type that is
not a default match for one of the ABL primitive types, ABL provides a data type keyword (
AS-data-type) that you must use to explicitly indicate the required .NET data type, as shown in
Table 25. (For example, the AS data type, UNSIGNED-BYTE, indicates a .NET System.Byte.)
AS [ CLASS
] object-type-name
Defines the property as an array of data elements, where the element data type is specified by either the AS
primitive-type-name option or the AS
object-type-name option. This option can specify an array property as either determinate (has a defined number of elements) or indeterminate (has an undefined number of elements). To define a determinate array property, specify the EXTENT option with the
constant argument. This optional argument is an integer value that represents the number of elements in the property array. To define an indeterminate array property, specify the EXTENT option without the
constant argument.
[ INITIAL
{ constant | [
constant [ ,
constant ] . . . ]
} ]
Table 36 lists the default initial values for the various property data types.
To use an accessor-access-mode with accessors:
So, if the property access mode is PUBLIC, any accessor-access-mode that you specify must be either PROTECTED or PRIVATE; if the property access mode is PROTECTED, any
accessor-access-mode that you specify must be PRIVATE, unless the property is abstract.
GET ( [ array-index-parameter ] ) : get-logic END [ GET ] .
|
Specifies an INPUT parameter that provides access to the index value of the current element of an array property from within the
get-logic. When a property is defined as an array using the EXTENT option, the GET accessor references the single element of the property array. The
array-index-parameter specifies the subscript value for the element being accessed. This is the syntax:
INPUT array-index-name AS { INTEGER | INT64 }
|
Can contain ABL code that executes for any operation that reads the property from outside its own GET accessor definition. This code can include any ABL statements that are valid in a method of a class, including CATCH and FINALLY blocks. However, if you define the property itself as static, you cannot access any instance members of classes (including the defining class) or use the SUPER and THIS-OBJECT system references; you can only access static class members and the local data elements of the GET accessor. The actual value that you return from the property is entirely dependent on your
get-logic code. Within
get-logic, any operation that reads from
property-name (such as the right-hand side of an assignment) directly reads the value of the property’s default memory. However, any operation that writes to
property-name within
get-logic invokes the property’s own SET accessor to assign the value, exactly like writing the property from outside of its own definition. If the SET accessor is defined with an implementation, that implementation determines how the value is written to the property.
You do not have to use the property’s default memory to provide the value read from a property. You can also use any other accessible data, such as a data member, as storage to access property values. To return a value from
get-logic to any outside reader of the property, you must return a value with the correct data type using the
RETURN statement (similar to any method that returns a value). If you do not invoke the RETURN statement in
get-logic, the property returns the Unknown value (
?).
SET ( parameter-definition [ , array-index-parameter ] ) :
set-logic END [ SET ] .
|
Specifies an INPUT parameter that provides access to the value written to the property from within the
set-logic. The
set-logic can contain ABL code that executes for any operation that writes to the property from outside its own SET accessor definition. This code can include any ABL statements that are valid in a method of a class, including CATCH and FINALLY blocks. However, if you define the property itself as static, you cannot access any instance members of classes (including the defining class) or use the SUPER and THIS-OBJECT system references; you can only access static class members and the local data elements of the SET accessor. To access the value being written to the property, specify
parameter-definition using the following syntax:
INPUT parameter-name AS
{ primitive-type-name | [ CLASS ] object-type-name }
|
AS { primitive-type-name | [ CLASS
] object-type-name }
Specifies an INPUT parameter that provides access to the index value of the current element of an array property from within the
set-logic. When a property is defined as an array using the EXTENT option, the SET accessor references a single element of the property array. The
array-index-parameter specifies the subscript value for the element being accessed. This is the syntax:
INPUT array-index-name AS { INTEGER | INT64 }
|
Can contain ABL code that accesses the value written to the property as parameter-name, then uses
parameter-name (if you choose) to set the new value for the property. The actual value that you use to set the property is entirely dependent on your
set-logic code. Within
set-logic, any operation that writes to
property-name (such as the left-hand side of an assignment) directly writes the specified value to the property’s default memory. However, any operation that reads from
property-name within
set-logic invokes the property’s own GET accessor to read the value, exactly like reading the property from outside of its own definition. If the GET accessor is defined with an implementation, that implementation determines the value that is read from the property.
The first example, shows a class (r-DefineProperties1) that defines a PUBLIC property (
cCurrentSalesRepName) followed by a procedure that accesses this property. The property is defined with two accessors without implementations, providing direct access to the property value. The GET accessor is PUBLIC, but the SET accessor is PRIVATE, allowing only the class to set the property value. In this case, the class sets the property from data (
RepName field) in the
sports2000 database provided by a buffer (
bSalesRep), which is PRIVATE.
The class also provides a PUBLIC method (getNextSalesRep( )) to read the
SalesRep table one record at a time and set the
cCurrentSalesRepName property to the value of the
RepName field for each record. This PUBLIC method also uses a PRIVATE method (
restartSalesRep( )) to reset the record position to the beginning of the table, based on an INPUT parameter. The class constructor also uses this PRIVATE method to initialize the record buffer to the first record in the table. The class sets the
cCurrentSalesRepName property to the Unknown value (
?) if the
SalesRep table is empty or
getNextSalesRep( ) reaches the end of the
SalesRep table.
CLASS r-DefineProperties1:
DEFINE PUBLIC PROPERTY cCurrentSalesRepName AS CHARACTER NO-UNDO
GET.
PRIVATE SET.
DEFINE PRIVATE BUFFER bSalesRep FOR SalesRep.
CONSTRUCTOR PUBLIC r-DefineProperties1 ():
restartSalesRep() NO-ERROR.
IF ERROR-STATUS:ERROR THEN RETURN ERROR ERROR-STATUS:GET-MESSAGE(1).
END CONSTRUCTOR.
METHOD PRIVATE VOID restartSalesRep ():
FIND FIRST bSalesRep NO-ERROR.
IF NOT AVAILABLE bSalesRep THEN DO:
cCurrentSalesRepName = ?.
RETURN ERROR "SalesRep table empty".
END.
ELSE
cCurrentSalesRepName = bSalesRep.RepName.
END METHOD.
METHOD PUBLIC VOID getNextSalesRep (INPUT lRestart AS LOGICAL):
IF lRestart THEN DO:
restartSalesRep() NO-ERROR.
IF NOT AVAILABLE bSalesRep THEN
RETURN ERROR ERROR-STATUS:GET-MESSAGE (1).
END.
ELSE DO:
FIND NEXT bSalesRep NO-ERROR.
IF NOT AVAILABLE bSalesRep THEN
cCurrentSalesRepName = ?.
ELSE
cCurrentSalesRepName = bSalesRep.RepName.
END.
END METHOD.
END CLASS.
|
The following procedure (r-runDefineProperties1.p) instantiates the
r-DefineProperties1 class, referenced by
clProps, and reads and displays the value of the
clProps:cCurrentSalesRepName property in a message, starting with the first
SalesRep record found as part of class instantiation. The procedure then displays the value of
clProps:cCurrentSalesRepName in a message for each record found by the
clProps:getNextSalesRep( ) method, restarting from the beginning of the
SalesRep table at the direction of the user.
In the next example, the r-DefineProperties2 class defines a PUBLIC property (
cNextSalesRepName) that provides the same data as the
cCurrentSalesRepName property defined by the
r-DefineProperties1 class. However, the GET accessor of the
cNextSalesRepName property is also used to provide the same access to the
SalesRep table that the
getNextSalesRep( ) method provides for the
r-DefineProperties1 class. So, the value of
cNextSalesRepName changes with each access.
CLASS r-DefineProperties2:
DEFINE PUBLIC PROPERTY lSalesRepRestart AS LOGICAL NO-UNDO
PRIVATE GET.
SET.
DEFINE PRIVATE BUFFER bSalesRep FOR SalesRep.
DEFINE PUBLIC PROPERTY cNextSalesRepName AS CHARACTER
GET ():
IF lSalesRepRestart THEN DO:
restartSalesRep() NO-ERROR.
IF NOT AVAILABLE(bSalesRep) THEN
RETURN ERROR ERROR-STATUS:GET-MESSAGE(1).
END.
ELSE DO:
FIND NEXT bSalesRep NO-ERROR.
IF NOT AVAILABLE(bSalesRep) THEN
cNextSalesRepName = ?.
ELSE
cNextSalesRepName = bSalesRep.RepName.
END.
RETURN cNextSalesRepName.
END GET.
PRIVATE SET.
CONSTRUCTOR PUBLIC r-DefineProperties2
(OUTPUT cFirstSalesRepName AS CHARACTER):
ASSIGN
lSalesRepRestart = TRUE
cFirstSalesRepName = cNextSalesRepName.
END CONSTRUCTOR.
METHOD PRIVATE VOID restartSalesRep ():
lSalesRepRestart = FALSE.
FIND FIRST bSalesRep NO-ERROR.
IF NOT AVAILABLE bSalesRep THEN DO:
cNextSalesRepName = ?.
RETURN ERROR "SalesRep table is empty".
END.
ELSE
cNextSalesRepName = bSalesRep.RepName.
END METHOD.
END CLASS.
|
Because this cNextSalesRepName property incorporates the record access provided by the
getNextSalesRep( ) method, the following procedure that accesses the
cNextSalesRepName property must also use the property in a manner similar to how the
r-runDefineProperties1.p procedure uses the
getNextSalesRep( ) method. As a result, the class also provides a second, publicly writable property (
lSalesRepRestart) to indicate when the reading of
SalesRep records must restart from the beginning of the table. (Note that the
getNextSalesRep( ) method provides its own INPUT parameter to indicate whether to restart record reading.)
Finally, the r-DefineProperties2 class constructor sets
lSalesRepRestart together with an initial read of the
cNextSalesRepName property in order to initialize the record buffer to the first record of the table and pass the associated
RepName field value to the instantiating procedure as an OUTPUT parameter.
Thus, the following procedure (r-runDefineProperties2.p) instantiates the
r-DefineProperties2 class, referenced by
clProps, and reads and displays the data from the
clProps:cNextSalesRepName property in a manner similar to how the
r-runDefineProperties1.p procedure reads and displays the same data using the
getNextSalesRep( ) method. However, because the
clProps:cNextSalesRepName property always returns the
RepName field for the
next SalesRep record in the table, the
r-runDefineProperties2.p procedure must provide a separate variable (
cCurrentSalesRepName) of its own, which provides the same function that the
cCurrentSalesRepName property provides for the
r-DefineProperties1 class, which is to maintain a current value read from the
RepName field.
DEFINE VARIABLE clProps AS CLASS r-DefineProperties2 NO-UNDO.
DEFINE VARIABLE cCurrentSalesRepName AS CHARACTER NO-UNDO.
clProps = NEW r-DefineProperties2(OUTPUT cCurrentSalesRepName) NO-ERROR.
DO WHILE NOT cCurrentSalesRepName = ?:
MESSAGE "The current sales rep is " cCurrentSalesRepName + ". Continue?"
VIEW-AS ALERT-BOX QUESTION BUTTONS YES-NO
UPDATE lContinue AS LOGICAL.
IF NOT lContinue THEN LEAVE.
ELSE DO:
cCurrentSalesRepName = clProps:cNextSalesRepName.
IF cCurrentSalesRepName = ? THEN DO:
MESSAGE "End of sales rep list. Restart?"
VIEW-AS ALERT-BOX QUESTION BUTTONS YES-NO
UPDATE lRestart AS LOGICAL.
IF lRestart THEN
ASSIGN
clProps:lSalesRepRestart = TRUE
cCurrentSalesRepName = clProps:cNextSalesRepName.
END.
END.
END.
|
Thus, the logic of the r-runDefineProperties2.p procedure is almost identical to the
r-runDefineProperties1.p procedure, reading a
clProps:cNextSalesRepName property instead of invoking a
clProps:getNextSalesRep( ) method in order to read an appropriate value from the
SalesRep table.
|
You can handle application errors in an a property accessor as in any ABL block. By executing a RETURN ERROR action at the block level or a THROW action at the block level with the presence of a ROUTINE-LEVEL ON ERROR UNDO, THROW statement, the AVM returns the ERROR condition to the statement that references the property and works much like an error raised by a method. If a RETURN ERROR also includes the option to return a character string value, or you set the ReturnValue property of a Progess.Lang.AppError object that you THROW, you can get this value using the RETURN-VALUE function following the statement that references the property or in a CATCH block that catches the Progress.Lang.AppError object. If the body of an accessor contains an UNDO block, any unhandled ERROR condition in that block undoes only the data within that block, according to the NO-UNDO setting of the data. The property value, itself, is not undone unless the property is defined without NO-UNDO. For more information, see OpenEdge Development: Object-oriented Programming.
|
|
If ERROR is raised during execution of an Assignment (=) statement, the value on the left-hand side usually remains unchanged from its value prior to the assignment. However, if the left-hand side of the assignment is a property and its SET accessor invokes the RETURN ERROR statement (raising ERROR on the assignment), the value of the property can be changed. This is because the SET accessor is a method, and like all methods that raise ERROR, any data elements that the SET accessor changes retain their most recent values after ERROR is raised. Thus, if the SET accessor changes the property value before invoking RETURN ERROR, the property retains its most recent change in value after ERROR is raised on the assignment.
|
|
If an ABL property that implements a property defined in a .NET interface is accessed from .NET and it raises ERROR out of the accessor block, ABL returns a .NET System.Exception to the caller. If the error is raised by executing a RETURN ERROR with the optional error string, the Message property of the System.Exception describes the operation where the error occurred, but the error string is available only to the ABL session, using the RETURN-VALUE function. If the error is raised by executing a RETURN ERROR with an optional ABL error object or by executing an UNDO, THROW, the System.Exception Message property includes both a description of the operation where the error occurred and any messages from the ABL error object. If the error is fatal, the AVM responds as for any ABL class, generating a protrace file and exiting the session.
|