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 OVERRIDEPROPERTY property-nameAS primitive-type-nameAS CLASS object-type-nameEXTENT constantINITIAL constant [ constant , constant ]NO-UNDOaccessor-access-mode GET implementation .SET implementation .GET implementation .accessor-access-mode SET implementation .
DEFINE PRIVATE PROTECTED PUBLIC STATIC ABSTRACT OVERRIDEPROPERTY property-nameAS primitive-type-nameAS CLASS object-type-nameEXTENT constantINITIAL constant [ constant , constant ]NO-UNDOGET implementation .
DEFINE PRIVATE PROTECTED PUBLIC STATIC ABSTRACT OVERRIDEPROPERTY property-nameAS primitive-type-nameAS CLASS object-type-nameEXTENT constantINITIAL constant [ constant , constant ]NO-UNDOSET implementation .
DEFINE PUBLIC PROPERTY property-nameAS primitive-type-name AS CLASS object-type-nameEXTENT constant NO-UNDOGET.SET.GET.SET.
DEFINE PROTECTED PUBLIC OVERRIDE ABSTRACT PROPERTY property-nameAS primitive-type-name AS CLASS object-type-nameEXTENT constant NO-UNDOGET.SET.GET.SET.PRIVATE PROTECTED PUBLICThis access mode applies to both reading and writing the property value by default. However, you can set a different access mode for reading or writing the property value (but not both) using an appropriate accessor-access-mode option to define the corresponding accessor (GET or SET).STATICFor more information on the mechanism for accessing properties of different access modes and scopes, see the reference entry for Class-based property access.ABSTRACTOVERRIDE
Note: A .NET abstract property or class is defined in C# with the abstract keyword.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: If the property is defined in a class that is derived from a .NET class, other restrictions on property-name apply. For more information, see the CLASS statement reference entry.AS primitive-type-nameSpecifies 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 DECIMALAS-data-typeHANDLE INT64 INTEGER LOGICAL LONGCHAR MEMPTR RAW RECID ROWIDIf 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.)
Note: At run time, a property defined using an AS-data-type keyword behaves in ABL like the corresponding ABL primitive type shown in Table 25. (For example, an UNSIGNED-BYTE behaves like an INTEGER.)Also note that when implementing a .NET array property, you must specify the .NET array object type (for example, "System.Int32[]" or "System.Byte[]"); you cannot use an ABL array equivalent (such as INTEGER EXTENT or UNSIGNED-BYTE EXTENT).These are the same set of primitive types that can be specified for the return type of a method. Thus, for example, a property cannot have a complex type such as a temp-table, ProDataSet, or any other data type that is invalid as a method return type. For more information on these primitive types, see the Data types reference entry.AS CLASS object-type-nameSpecifies an object reference with the data type of a class or interface for the default memory of the property. The default value of the property is the Unknown value (?). You cannot assign an initial value using the INITIAL option.Specifies the type name of an ABL or .NET class or interface. Specify an object type name using the syntax described in the Type-name syntax reference entry. With an appropriate USING statement, you can also specify a class or interface name alone, without the qualifying package or namespace.If you are overriding a .NET abstract property or implementing a property defined in a .NET interface, object-type-name must specify the exact same .NET object type as the corresponding interface property. However, for .NET inner (nested) type, note the difference in the ABL syntax, which replaces the corresponding period (.) in the .NET object type with a plus (+) (see the Type-name syntax reference entry).Also note that when implementing a .NET array property, you must specify the exact .NET array object type (for example, "System.Drawing.Point[]"); you cannot use an ABL array equivalent (such as System.Drawing.Point EXTENT).constantEXTENTDefines 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.The EXTENT is part of the property data type. For more information, see the Type-name syntax reference entry.If you do not use the EXTENT option (or you specify constant as 0), the property is not an array property.constant [ constant , constant . . . ]INITIALThe initial value of the property you want to define. If you use the AS primitive-type-name option and you do not use the INITIAL constant option, the initial value is the default initial value for the data type of the variable or array element.
DEFINE VARIABLE array-var AS CHARACTER NO-UNDO EXTENT 3INITIAL ["Add","Delete","Update"].
DEFINE VARIABLE x AS INTEGER NO-UNDO EXTENT INITIAL [1,2,3].DEFINE VARIABLE x1 AS INTEGER NO-UNDO EXTENT 3 INITIAL [1,2,3].You can also use the EXTENT statement to fix the number of elements in an unfixed indeterminate array variable. For more information, see the EXTENT statement reference entry.Table 36 lists the default initial values for the various property data types.
Table 33: Unknown value (?) (displays as blanks)
If you display a class instance using the MESSAGE statement, ABL automatically invokes the ToString( ) method (provided by the Progress.Lang.Object class) on the object reference. To display a class instance in a frame (for example, using the DISPLAY statement), you must first explicitly convert the object reference to a displayable type using the INT64 function, the INTEGER function, the STRING function, or the ToString( ) method.
NO-UNDOaccessor-access-mode
PROTECTED PRIVATETo use an accessor-access-mode with accessors:
If you define two accessors, you can specify an accessor-access-mode for one of the accessors, but only if this accessor-access-mode is more restrictive than the access mode of the property.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 implementation .You can define a GET accessor using two basic forms: with or without an implementation. However, if you are defining an abstract property or an interface property prototype, you cannot define the GET accessor with an implementation. This is the syntax for a GET accessor with an implementation:
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 INT64Specifies the name of the INPUT parameter that provides the subscript value (index) for this array element. You must use this value in a subscript on the property name (property-name[ array-index-name ]) to access the default memory for the current array element.INTEGER INT64Can 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 (?).This is the syntax for a GET accessor without an implementation:
GET.If the property is an array, the default accessor implementation handles the subscript references automatically. Providing an array-index-parameter in this case is a compiler error.SET implementation .You can define a SET accessor using two basic forms: with or without an implementation. However, if you are defining an abstract property or an interface property prototype, you cannot define the SET accessor with an implementation. This is the syntax for a SET accessor with an implementation:
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 ASprimitive-type-name CLASS object-type-nameINPUT parameter-nameSpecifies the name of the INPUT parameter that you reference in set-logic to obtain the value written to the property.Specifies the data type of the parameter as a built-in primitive type (primitive-type-name), or as a built-in or user-defined object type (object-type-name)). You must specify exactly the same data type as you have defined for the property.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 INT64Specifies the name of the INPUT parameter that provides the subscript value (index) for this array element. You must use this value in a subscript on the property name (property-name[ array-index-name ]) to access the default memory for the current array element.INTEGER INT64Can 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.You do not have to use the property’s default memory to store the property value. You can use any other accessible data, such as a data member, as storage to store property values. If you do not write a value to property-name within set-logic, the property’s default memory retains whatever value it had prior to any operation that writes to the property.This is the syntax for a SET accessor without an implementation:
SET.If the property is an array, the default accessor implementation handles the subscript references automatically. Providing an array-index-parameter in this case is a compiler error.
Note: This is equivalent to, but more efficient than, using an implementation to explicitly assign property-name the value of parameter-name.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 or an instance of 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-UNDOGET.
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.ELSEcCurrentSalesRepName = bSalesRep.RepName.END METHOD.METHOD PUBLIC VOID getNextSalesRep (INPUT lRestart AS LOGICAL):IF lRestart THEN DO:restartSalesRep() NO-ERROR.IF NOT AVAILABLE bSalesRep THENRETURN ERROR ERROR-STATUS:GET-MESSAGE (1).END.ELSE DO:FIND NEXT bSalesRep NO-ERROR.IF NOT AVAILABLE bSalesRep THENcCurrentSalesRepName = ?.ELSEcCurrentSalesRepName = 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.
DEFINE VARIABLE clProps AS CLASS r-DefineProperties1 NO-UNDO.clProps = NEW r-DefineProperties1() NO-ERROR.
IF ERROR-STATUS:ERROR THEN DO:MESSAGE ERROR-STATUS:GET-MESSAGE(1) VIEW-AS ALERT-BOX.QUIT.END.DO WHILE NOT clProps:cCurrentSalesRepName = ?:MESSAGE "The current sales rep is "clProps:cCurrentSalesRepName + ". Continue?"VIEW-AS ALERT-BOX QUESTION BUTTONS YES-NO UPDATE lContinue AS LOGICAL.IF NOT lContinue THEN LEAVE.ELSE DO:clProps:getNextSalesRep(FALSE).IF clProps:cCurrentSalesRepName = ? THEN DO:MESSAGE "End of sales rep list. Restart?"VIEW-AS ALERT-BOX QUESTION BUTTONS YES-NOUPDATE lRestart AS LOGICAL.IF lRestart THENclProps:getNextSalesRep(TRUE).END.END.END.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-UNDOPRIVATE GET.
SET.
DEFINE PRIVATE BUFFER bSalesRep FOR SalesRep.DEFINE PUBLIC PROPERTY cNextSalesRepName AS CHARACTERGET ():IF lSalesRepRestart THEN DO:restartSalesRep() NO-ERROR.IF NOT AVAILABLE(bSalesRep) THENRETURN ERROR ERROR-STATUS:GET-MESSAGE(1).END.ELSE DO:FIND NEXT bSalesRep NO-ERROR.IF NOT AVAILABLE(bSalesRep) THENcNextSalesRepName = ?.ELSEcNextSalesRepName = bSalesRep.RepName.END.RETURN cNextSalesRepName.END GET.PRIVATE SET.
CONSTRUCTOR PUBLIC r-DefineProperties2(OUTPUT cFirstSalesRepName AS CHARACTER):ASSIGNlSalesRepRestart = TRUEcFirstSalesRepName = 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.ELSEcNextSalesRepName = 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-NOUPDATE 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-NOUPDATE lRestart AS LOGICAL.IF lRestart THENASSIGNclProps:lSalesRepRestart = TRUEcCurrentSalesRepName = 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.For more examples of property definitions, including static and abstract properties, see the descriptions of r-CustObj.cls, r-CustObjStatic.cls, and r-CustObjAbstract.cls in the CLASS statement reference entry.
PUBLIC and PROTECTED properties defined within a class definition (.cls) file maintain their characteristics throughout the inherited class hierarchy. Thus, you cannot shadow (override) properties in a subclass that are defined in a super class.
You never invoke the GET and SET accessors defined for a property directly. These methods are only invoked implicitly when you read (GET) or write (SET) the value of the property. For example, you can read or write the property by using the property in an Assignment (=) statement or by passing the property as a parameter to a method, procedure, or user-defined function. For information on reading and writing property values, see the Class-based property access reference entry. As an alternative, you can define a method in a class to read or write the value of a data member that you separately define in the same class.
If the property name is an ABL reserved keyword, you might need to use the THIS-OBJECT system reference or Type-name syntax to reference the property. For more information on referencing properties, see the reference entry for Class-based property access.
a)
b)
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.
© 2013 Progress Software Corporation and/or its subsidiaries or affiliates. |