PreviousNextIndex

DEFINE PROPERTY statement

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.

Note: This statement is applicable only when used in a class or interface definition (.cls) file.
Syntax

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

Use the following syntax to define a property that you can read only:

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

Use the following syntax to define a property that you can write only:

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

Use the following syntax to declare an interface property prototype:

DEFINE [ PUBLIC ] PROPERTY property-name  
  { { AS primitive-type-name | AS [ CLASS ] object-type-name } 
     [ EXTENT [ constant ] ] [ NO-UNDO ] } 
  { GET. 
     SET. 
    | 
    GET. 
    | 
    SET. } 

Use the following syntax to declare an abstract property prototype:

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 ]
[ STATIC ]
[ ABSTRACT ]
[ OVERRIDE ]
property-name
AS primitive-type-name
AS [ CLASS ] object-type-name
object-type-name
CLASS
[ EXTENT [ constant ] ]
[ INITIAL { constant | [ constant [ , constant ] . . . ] } ]
[ NO-UNDO ]
[ accessor-access-mode ]
GET [ implementation ] .
array-index-name
INTEGER | INT64
SET [ implementation ] .
parameter-definition
INPUT parameter-name
AS { primitive-type-name | [ CLASS ] object-type-name }
array-index-parameter
array-index-name
INTEGER | INT64
set-logic
Examples

The examples that follow show two different ways to access the same private data of a class using properties.

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.

r-DefineProperties1.cls
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.

r-runDefineProperties1.p
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-NO  
        UPDATE lRestart AS LOGICAL. 
      IF lRestart THEN 
        clProps: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.

r-DefineProperties2.cls
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.

r-runDefineProperties2.p
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.

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.

Notes
See also

Assignment (=) statement, Class-based property access, Data types, DEFINE VARIABLE statement, DISPLAY statement, METHOD statement, RETURN statement


OpenEdge Release 10.2B
Copyright © 2009 Progress Software Corporation
PreviousNextIndex