Function ClassCreate() Foundation

Creates a class dynamically (the class object).

Syntax
ClassCreate( <cClassName>   , ;
            [<aSuperClass>] , ;
            [<aMember>]     , ;
            [<aMethod>] ) --> oClassObject | NIL
Parameters
<cClassName>
The character expression <cClassName> indicates the name of the class to be created.
<aSuperClass>
If a class is to be derived from other classes, a one dimensional array <aSuperClass> whose elements reference class objects of the super classes must be specified.
<aMember> := { {<cName>,<nAttr>}, ... }
Member variables of the class are defined using a two column array <aMember>. The first column contains the names of the member variables as character strings (<cName>), while numeric values in the second column (<nAttr>) define attributes like visibility or access rights. If an empty array or NIL is specified for <aMember>, the new class has no member variables.
Constants defined in CLASS.CH must be used for <nAttr>. The constants can be added in order to specify multiple attributes for one member variable. The following tables list the possible #define constants for <nAttr>:
Constants for member variable visibility
Constant Equivalent in the class declaration
CLASS_HIDDEN *) HIDDEN:
CLASS_PROTECTED PROTECTED:
CLASS_EXPORTED EXPORTED:
  1. Default
Constants for member variable assignment rights
Constant Equivalent in the class declaration
VAR_ASSIGN_HIDDEN VAR <cName> ASSIGNMENT HIDDEN
VAR_ASSIGN_PROTECTED VAR <cName> ASSIGNMENT PROTECTED
VAR_ASSIGN_EXPORTED VAR <cName> ASSIGNMENT EXPORTED
Constants for member variable types
Constant Equivalent in the class declaration
VAR_INSTANCE *) VAR <cName>
VAR_CLASS CLASS VAR <cName>
VAR_CLASS_SHARED CLASS VAR <cName> SHARED
  1. Default
The attribute for assignment defaults to the visibility attribute.
<aMethod> := { {<cMethod>,<nAttr>,<bBlock>[,<cMember>]}, ... }
Data for the definition of methods available in the class must be specified using a two-dimensional array <aMethod>. Each element contains an array with three or four elements. The first element of a sub-array is a character string with the name of a method (<cMethod>); the second element is a numeric value (<nAttr>); the third element is a code block (<bBlock>) which is evaluated when the method is called; and the optional fourth element is a character string indicating the name of a member variable that is linked to the method (redirection for ACCESS/ASSIGN methods).
As with member variables, constants must be used for <nAttr> which may be added if one method is to have multiple attributes. All available #define constants are shown below:
Constants for method visibility
Constant Equivalent in the class declaration
CLASS_HIDDEN *) HIDDEN:
CLASS_PROTECTED PROTECTED:
CLASS_EXPORTED EXPORTED:
  1. Default
Constants for method type
Constant Equivalent in the class declaration
METHOD_INSTANCE *) METHOD <cMethod>
METHOD_CLASS CLASS METHOD <cMethod>
  1. Default
Constants for redirection
Constant Equivalent in the class declaration
METHOD_ACCESS ACCESS METHOD <cMethod> [VAR <cMember>]
METHOD_ASSIGN ASSIGN METHOD <cMethod> [VAR <cMember>]
  1. Default
Each code block <bBlock> receives as its first parameter the object (self) when a method is called. All following code block parameters are identical to the parameters passed to the method.
Return

When a class is created, the function returns the corresponding class object. If the class <cClassName> exists already, NIL is returned.

Description

The function ClassCreate() creates classes dynamically at runtime of a program. Dynamic classes have no class functions, they are represented only by class objects. A class function exists when classes are known at compile time. This applies to implicitly existing classes - like TBrowse() or XbpDialog() - as well as to those classes which are explicitly declared within CLASS..ENDCLASS statements.

Data for the definition of member variables and methods must be passed to ClassCreate() in the form of two dimensional arrays. The program code for methods is then represented by a code block whose first parameter is reserved. The first parameter receives the object (Self) when the method is called. If a method call includes additional parameters, they are passed on to the code block after Self (the first method parameter is the second code block parameter etc.)

When a class object is created, it remains in main memory regardless of whether or not it is referenced in a variable. It can be retrieved by the function ClassObject() at any time. This distinguishes a class object from its instances. Instances of a class are automatically removed from memory as soon as they are no longer referenced in a variable. A class object, however, must be removed explicitly by calling ClassDestroy() when the class is no longer needed in a program.

Dynamic classes have the inherent disadvantage that they cannot be optimized by the compiler. It is therefore recommended, to declare classes within CLASS..ENDCLASS whenever this is possible.

Examples
Dynamic class creation

// The example implements the user-defined function DbRecord() 
// which creates a class object for an open database. Objects 
// of the class have instance variables whose names match with 
// the field names of the database. The program code for the 
// methods :init(), :get() and :put() is provided by code blocks 
// which call STATIC functions. There, the instance variables 
// of an object are accessed using the macro operator. 

#include "Class.ch" 

PROCEDURE Main 
   LOCAL oRecord 
   CLS 
   USE Customer 

   ? Alias()                        // Result: CUSTOMER 
   ? FIELD->LASTNAME                // Result: Miller 

   oRecord := DbRecord():new() 

   ? oRecord:className()            // Result: CUSTOMER 
   ? oRecord:lastname               // Result: Miller 
   ? oRecord:lastname := "Jones"    // Result: Jones 
   ? oRecord:put()                  // Result: .T. 

   ? FIELD->LASTNAME                // Result: Jones 

   ? Recno()                        // Result: 1 
   oRecord:skip( 10 ) 
   ? Recno()                        // Result: 11 
   USE 
RETURN 


** Return class object for database in the current work area 
** and create it if necessary. 
FUNCTION DbRecord() 
   LOCAL aIVar, aMethod, oClass, nAttr, bSkip 

   oClass := ClassObject( Alias() ) 

   IF oClass <> NIL 
      RETURN oClass                 // Class already exists 
   ENDIF 

   nAttr   := CLASS_EXPORTED + VAR_INSTANCE 
   aIVar   := AEval( DbStruct(), {|a| a:={a[1], nAttr} } ,,, .T.) 

   nAttr   := CLASS_EXPORTED + METHOD_INSTANCE 
   aMethod := {{ "INIT" , nAttr, {|self| GetRecord(self) } }, ; 
               { "GET"  , nAttr, {|self| GetRecord(self) } }, ; 
               { "PUT"  , nAttr, {|self| PutRecord(self) } }  } 

   // Method with parameter according to obj:skip( n ) 
   bSkip   := {|self,n| DbSkip(n), ::get() } 
   AAdd( aMethod, { "SKIP" , nAttr, bSkip } ) 

RETURN ClassCreate( Alias(),, aIVar, aMethod ) 


** Transfer values from fields to instance variables 
STATIC FUNCTION GetRecord( oRecord ) 
   AEval( DbStruct(), {|a,i| oRecord:&(a[1]) := FieldGet(i) } ) 
RETURN oRecord 


** Transfer values from instance variables to fields 
STATIC FUNCTION PutRecord( oRecord ) 
   LOCAL lLocked := RLock() 
   IF lLocked 
      AEval( DbStruct(), {|a,i| FieldPut(i, oRecord:&(a[1])) } ) 
      DbUnlock() 
   ENDIF 
RETURN lLocked 
Feedback

If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.