CLASS (object declaration)

Top  Previous  Next

 

label

CLASS( [ parentclass ] )

[,EXTERNAL] [,IMPLEMENTS] [,DLL( )] [,STATIC] [,THREAD] [,BINDABLE]

 

 

[,MODULE( )] [, LINK( )] [, TYPE] [, DIM(dimension)] [,NETCLASS] [,PARTIAL]

 

[ data members and methods ]

 

 

END

 

CLASS

An object containing data members and methods that operate on the data.

parentclass

The label of a previously declared CLASS structure whose data and methods the new CLASS inherits. This may be a CLASS with the TYPE attribute.

EXTERNAL

Specify the object is defined, and its memory is allocated, in an external library.

IMPLEMENTS

Specify an INTERFACE for the CLASS. This adds additional methods to the implementation of the CLASS.

DLL

Specify the object is defined in a .DLL. This is required in addition to the EXTERNAL attribute.

STATIC

Specify the data members' memory is permanently allocated.

THREAD

Specify memory for all class variables are allocated once for each execution thread. Also causes Constructors and Destructors to be called on thread start and exit.

BINDABLE

Specify all variables in the class may be used in dynamic expressions.

MODULE

Specify the source code module containing the CLASS's member PROCEDURE definitions. This serves the same function as the MODULE structure within a MAP structure. If omitted, the member PROCEDURE definitions must all be in the same source code module containing the CLASS declaration.

LINK

Specify the source code module containing the CLASS's member PROCEDURE definitions is automatically added to the compiler's link list. This eliminates the need to specifically add the file to the project.

TYPE

Specify the CLASS is only a type definition and not also an object instance of the CLASS.

data members and methods

Data declarations and PROCEDURE prototypes. The data members may only be data declarations appropriate to a GROUP structure, and may include references to the same class (recursive classes). The WHAT and WHERE procedures allow access to the data members by their relative position within the CLASS structure.

DIM

Declares a CLASS as an array.

dimension

A positive numeric constant which specifies the number of elements in this dimension of the array.

NETCLASS

Switch off the generation of additional Clarion–specific code for the CLASS

PARTIAL

Identifies a CLASS definition that is split into more than one physical file

 

A CLASS structure declares an object which contains data members (properties) and the methods (PROCEDUREs) that act on that data. A CLASS structure must be terminated by a period or the END statement.

Derived CLASSes (Inheritance)

A CLASS declared with the parentclass parameter creates a derived class which inherits all the data members and methods of the named parentclass. The derived class may also contain its own data members and methods.

All data members explicitly declared in the derived class create new variables, and cannot be declared with the same labels as data members in the parentclass.

Any method prototyped in the derived class with the same name as a method in the parentclass overrides the inherited method if both have the same parameter lists. If the two methods have different parameter lists, they create polymorphic functions in the derived class that must obey the rules of Procedure Overloading.

Object Properties (Encapsulation)

Each instance of a CLASS, whether a base class, derived class, or a declared instance of either, contains its own set of data members (properties) specific to that instance. These may be private or public. However, there is only one copy of any inherited methods (residing in the CLASS that declared it) which any instance of that CLASS, or any of its derived classes, calls.

The methods of a CLASS with the TYPE attribute cannot be directly called (as ClassName.Method)--they must be called only as a member methods of the objects declared as the type (as Object.Method).

VIRTUAL Methods (Polymorphism)

If there is a method prototyped in the CLASS with the same label as a method in the parentclass with the VIRTUAL attribute, it must also be prototyped with the VIRTUAL attribute in the derived class.

The VIRTUAL attribute on both prototypes creates virtual methods that allow the methods in a parentclass to call the same named VIRTUAL methods in the derived class to perform functions specific to the derived class that the parentclass does not know about.

VIRTUAL methods in the derived class may directly call the parentclass method of the same name by prepending PARENT to the method's name. This allows incremental derivation wherein a derived class method may simply call down to the parentclass method to perform its functionality, then extend it for the requirements of the derived class.

VIRTUAL methods in the derived class may optionally use the DERIVED attribute. This will cause the compiler to verify that a virtual method with the same name and calling signature exists in the base class. This prevents potential issues that can end up defining new virtual methods instead of overriding existing ones.

Scoping Issues

The scope of an object is dependent upon where it is declared. Generally, a declared object comes into scope at the CODE statement following its declaration and goes out of scope at the end of the related executable code section. A dynamically instantiated object (using NEW) shares the scope of the executable code section in which it is instantiated.

An Object declared:

·

As Global data is in scope throughout the application.

·

As Module data is in scope throughout the module.

·

As Local data is in scope only in the procedure, except ...

Methods prototyped in a derived CLASS declaration within a procedure's Local data section are Local Derived Methods and share the declaring procedure's scope for all local data declarations and routines. The methods must be defined within the same source module as the procedure within which the CLASS is declared and must immediately follow the procedure within that source--that is, they must come after any ROUTINEs and before any other procedures that may be in the same source module. This means the procedure's Local data declarations and ROUTINEs are all visible and can be referenced within these methods.

Example:

SomeProc    PROCEDURE

MyLocalVar  LONG

MyDerivedClass CLASS(MyClass)  !Derived class with a virtual method

MyProc          PROCEDURE,VIRTUAL

              END

CODE

!SomeProc main executable code goes here

!SomeProc ROUTINEs goes here

MyRoutine ROUTINE

!Routine code goes here

 

!MyDerivedClass methods immediately follow:

 

MyDerivedClass.MyProc PROCEDURE

CODE

MyLocalVar = 10               !MyLocalVar is still in scope, and available for use

DO MyRoutine                  !MyRoutine is still in scope, and available for use

 

!Any other procedures in the same module go here, following all

!derived class methods

 

Instantiation

You declare an instance of a CLASS (an object) by simply naming the CLASS as the data type of the new instance, or by executing the NEW procedure in a reference assignment statement to a reference variable for that named CLASS. Either way, the new instance inherits all the data members and methods of the CLASS for which it is an instance. All the attributes of a CLASS except MODULE and TYPE are valid on an instance declaration.

If there is no TYPE attribute on the CLASS, the CLASS structure itself declares both the CLASS and an object instance of that CLASS. A CLASS with the TYPE attribute does not create an object instance of the CLASS.

For example, the following CLASS declaration declares the CLASS as a data type and an object of that type:

MyClass  CLASS       !Both a data type declaration and an object instance

MyField   LONG

MyProc    PROCEDURE

        END

while this only declares the CLASS as a data type:

MyClass  CLASS,TYPE  !Only a data type declaration

MyField   LONG

MyProc    PROCEDURE

        END

It is preferable to directly declare object instances as the CLASS data type rather than as a reference to the CLASS. This results in smaller quicker code and does not require you to use NEW and DISPOSE to explicitly create and destroy the object instance. The advantage of using NEW and DISPOSE is explicit control over the lifetime of the object. For example:

MyClass  CLASS,TYPE

MyField   LONG

MyProc    PROCEDURE

        END

OneClass MyClass           !Declared object instance, smaller and quicker

TwoClass &MyClass          !Object reference, must use New and DISPOSE

CODE

!execute some code here

TwoClass &= NEW(MyClass)  !The lifetime of the object starts here

!execute some code here

DISPOSE(TwoClass)         ! and extends only to here

!execute some code here

Another advantage of declaring the object is the ability to declare the object with any of the attributes available for the CLASS declaration itself (except TYPE and MODULE). For instance, you can declare an object with the THREAD attribute, whether the CLASS is declared with THREAD or not.

The lifetime of an object depends on how it is instantiated:

·

An object declared in the Global data section or a Module's data section is instantiated at the CODE statement following the PROGRAM statement and de-instantiated when the application terminates.

·

A reference to an object is instantiated by the NEW statement, and de-instantiated by the DISPOSE statement.

·

An object declared in a procedure's Local data section is instantiated at the CODE statement following the PROCEDURE statement and de-instantiated when a RETURN (implicit or explicit) executes to terminate the procedure.

Threading

The constructors and destructors for threaded classes are called for every thread. Every new thread gets new instances of CLASSes and variables declared at the global or module level with the THREAD attribute. The RTL calls constructors for the threaded classes when the thread is started and the destructors when the thread is ended. In previous Clarion versions they were called only when the main thread started and ended.

 

Data (Property) Initialization

The simple data type data members of an object are automatically allocated memory and initialized to blank or zero (unless the AUTO attribute is specified) when the object comes into scope. The allocated memory is returned to the operating system when the object goes out of scope.

The reference variable data members of an object are not allocated memory and are not initialized when the object comes into scope--you must specifically execute a reference assignment or a NEW statement. These references variables are not automatically cleared when the object goes out of scope, so you must DISPOSE of all NEWed properties before the object goes out of scope.

Constructors and Destructors

A CLASS method labelled "Construct" is a constructor method which is automatically invoked when the object comes into scope, immediately after the data members of the object are allocated and initialized.

The "Construct" method may not receive any parameters and may not use the VIRTUAL attribute. You may explicitly call the "Construct" method in addition to its automatic invocation.

Multiple Constructors with parameters are now supported.

If an object is an instance of a derived CLASS and both the parentclass and the derived CLASS contain constructors and the derived CLASS's constructor does not have the REPLACE attribute, then the parentclass constructor is automatically invoked at the beginning of the derived CLASS's constructor. If the derived CLASS's constructor does have the REPLACE attribute, then only derived CLASS's constructor is automatically invoked (the derived CLASS's constructor method can explicitly call PARENT.Construct if it needs to).

A CLASS method labelled "Destruct" is a destructor method which is automatically invoked when the object leaves scope, immediately before the data members of the object are de-allocated. The "Destruct" method may not receive any parameters. You may explicitly call the "Destruct" method in addition to its automatic invocation.

If an object is an instance of a derived CLASS and both the parentclass and the derived CLASS contain destructors and the derived CLASS's destructor does not have the REPLACE attribute, then the parentclass destructor is automatically invoked at the end of the derived CLASS's destructor. If the derived CLASS's destructor does have the REPLACE attribute, then only derived CLASS's destructor is automatically invoked (the derived CLASS's destructor method can explicitly call PARENT.Destruct if it needs to).

Public, PRIVATE, and PROTECTED (Encapsulation)

Public data members and methods of a CLASS or derived CLASS are declared without either the PRIVATE or PROTECTED attributes. Public data members and methods are visible to all the methods of the declaring CLASS, and derived CLASSes, and any code where the object is in scope.

Private data members and methods are declaredwith the PRIVATE attribute. Private data members and methods are visible only to the methods of the CLASS within which they are declared and any other procedures contained in the same source code module.

Protected data members and methods are declared with the PROTECTED attribute. Protected data members and methods are visible only to the methods of the CLASS within which they are declared, and to the methods of any CLASS derived from the CLASS within which they are declared.

Method Definition

The PROCEDURE definition of a method (its executable code, not its prototype) is external to the CLASS structure. The method's definition must either prepend the label of the CLASS to the label of the PROCEDURE, or name the CLASS (and label it SELF) as the first (implicit) parameter in the list of parameters passed in to the PROCEDURE.

Remember that on the PROCEDURE definition statement you are assigning labels for use within the method to all the passed parameters, and so, since the CLASS's label is the data type of the implicit first parameter, you must use SELF as the assigned label for the CLASS name parameter. For example, for the following CLASS declaration:

MyClass CLASS

MyProc   PROCEDURE(LONG PassedVar)             !The method takes 1 parameter

       END

you may define the MyProc PROCEDURE either as:

MyClass.MyProc PROCEDURE(LONG PassedVar)       !Prepend the CLASS name to

CODE                                          !the method's label

or as:

MyProc PROCEDURE(MyClass SELF, LONG PassedVar) !The CLASS name is the

CODE

! implicit first parameter's data type, labeled SELF

 

Referencing an Object's properties and methods in your code

You must reference the data members of a CLASS using Clarion's Field Qualification syntax. To do this, you prepend the label of the CLASS (if it is an object instance of itself) or the label of an object instance of the CLASS to the label of the data member.

For example, for the following CLASS declarations:

MyClass  CLASS         !Without TYPE, this is also an object instance

MyField   LONG         ! in addition to a class type declaration

MyProc    PROCEDURE

        END

MyClass2 MyClass       !Declare another object instance of MyClass

you must reference the two MyField variables from procedures external to the object as:

MyClass.MyField = 10   !References the MyClass CLASS declaration's object

MyClass2.MyField = 10  !References the MyClass2 declaration's object

You may call the methods of a CLASS either using Field Qualification syntax (by prepending the label of the CLASS to the label of the method), or by passing the label of the CLASS as the first (implicit) parameter in the list of parameters passed to the PROCEDURE.

For example, for the following CLASS declaration:

MyClass  CLASS

MyProc    PROCEDURE

        END

you may call the MyProc PROCEDURE either as:

CODE

MyClass.MyProc

or as:

CODE

MyProc(MyClass)

SELF and PARENT

Within the methods of a CLASS, the data members and methods of the current object's instance are referenced with SELF prepended to their labels instead of the name of the CLASS. This allows the methods to generically reference the data members and methods of the currently executing instance of the CLASS, without regard to whether it is executing the parentclass, a derived class, or any instance of either. This is also the mechanism that allows a parentclass to call virtual methods of a derived class.

For example, expanding on the previous example, MyField is referenced within the MyClass.MyProc method as:

MyClass.MyProc PROCEDURE

CODE

SELF.MyField = 10 !Assign to the current object instance's property

The data members and methods of a parentclass can be directly referenced from within the methods of a derived class with PARENT prepended to their labels instead of SELF.

For example:

MyDerivedClass.MyProc PROCEDURE

CODE

!execute some code

PARENT.MyProc       !Call the base class method

!execute some more code

 

Methods of derived CLASSes cannot have a formal parameter with the label PARENT, and additionally no method can have an explicit formal parameter with the label SELF.

For example:

MyDerivedClass.MyProc PROCEDURE(MyDerivedClass Parent) !Illegal use of Parent

 

The compiler produces a "Redefining system intrinsics" warning for any explicit attempt to declare any data with the labels SELF or PARENT used in this context.

 

Nested CLASSes

In Clarion .Net nested classes are supported. You can declare a class inside of another class. The only restriction is that only inline methods, properties, and indexers can be used in the nested class. See also INLINE.

 

CLASS Conceptual Example:

     PROGRAM

     MAP.                                       !MAP required to get BUILTINS.CLW

 

OneClass CLASS                                   !Base class

NameGroup GROUP                                  !Reference as OneClass.NameGroup

First      STRING(20)                            !reference as OneClass.NameGroup.First

Last       STRING(20)                            !reference as OneClass.NameGroup.Last

         END

BaseProc  PROCEDURE(REAL Parm)                   !Declare method prototype

Func      PROCEDURE(REAL Parm),STRING,VIRTUAL    !Declare virtual method prototype

Proc      PROCEDURE(REAL Parm),VIRTUAL           !Declare virtual method prototype

        END                                     !End CLASS declaration

 

TwoClass  CLASS(OneClass),MODULE('TwoClass.CLW') !Derived from OneClass

Func       PROCEDURE(LONG Parm),STRING           !replaces OneClass.Func

Proc       PROCEDURE(STRING Msg,LONG Parm)       !Functionally overloaded

         END

 

ClassThree CLASS(TwoClass),MODULE('Class3.CLW')  !Derived from TwoClass

Func        PROCEDURE(<STRING Msg>,LONG Parm),STRING,VIRTUAL

Proc        PROCEDURE(REAL Parm),VIRTUAL

          END

 

ClassFour  ClassThree                            !Declare an instance of ClassThree

ClassFive  ClassThree                            !Declare an instance of ClassThree

 

CODE

OneClass.NameGroup =   '|OneClass Method'       !Assign values to each instance of NameGroup

TwoClass.NameGroup =   '|TwoClass Method'

ClassThree.NameGroup = '|ClassThree Method'

ClassFour.NameGroup =  '|ClassFour Method'

MESSAGE(OneClass.NameGroup & OneClass.Func(1.0))!Calls OneClass.Func

MESSAGE(TwoClass.NameGroup & TwoClass.Func(2))  !Calls TwoClass.Func

 

MESSAGE(ClassThree.NameGroup & ClassThree.Func('|Call ClassThree.Func',3.0))

!Calls ClassThree.Func

 

MESSAGE(ClassFour.NameGroup & ClassFour.Func('|Call ClassFour.Func',4.0))

!Also Calls ClassThree.Func

 

OneClass.BaseProc(5)                            !BaseProc Calls OneClass.Proc & Func

BaseProc(TwoClass,6)                            !BaseProc Also calls OneClass.Proc & Func

TwoClass.Proc('Second Class',7)                 !Calls TwoClass.Proc (overloaded)

ClassThree.BaseProc(8)                          !BaseProc Calls ClassThree.Proc & Func

ClassFour.BaseProc(9)                           !BaseProc Also Calls ClassThree.Proc & Func

Proc(ClassFour,'Fourth Class',10)               !Calls TwoClass.Proc (overloaded)