INTERNAL METHODS AND INSTANCE VARIABLES:
----------------------------------------
Every class created with oObject contains a number of internal IVars
and methods :
(1) ::Dict This is an IVar that contains the _OBJECT_ object
that was used to create the class. It is used
internally to access the object but also contains some
useful info regarding the class:
::Dict:Name -> the class Name (like ::className())
::Dict:ClsAlias -> the class Alias
::Dict:Handle -> the class Handle (like ::ClassH())
::Dict:NewObj -> a copy of self
::Dict:Super -> the parent class instance
::Dict:bSuper -> the code block used to create the parent class instance
::Dict:Scope -> Last value of Scope
::Dict:nElem -> Total IVars of class
::Dict:nMElem -> Total Methods of class
::Dict:IVars -> Array of class IVars. Elements:
{
IVar Name,
INLINE/BLOCK/STATIC Function,
Initial Value,
Scope, ;
Linked Method,
nPos,
Alias
}
::Dict:Methods -> Array of class Methods. Elements:
{
Method Name,
Method Function,
Method Alias,
Method Scope
}
(2) ::Super The immediate parent class if any was used. Also to support
::<Alias>Super Multiple inheritance, an IVar called ::<ClassAlias>+"SUPER"
is created for every class up the foodchain.
Supposing class DIALOG inherits from SCREEN->BOX->WINDOW
we may send a message directly to the BOX class thus
overriding the immediate parent class:
::Super:Display() -> calls WINDOW():Display()
(the immediate parent)
::BoxSuper:Display() -> calls BOX():Display()
(4) ::__Super() A METHOD that can be used to jump to parent classes
methods, however it is a bit slower than the
alternative ways to do that described above.
Arguments:
The name of the super class.
Supposing class DIALOG inherits from SCREEN->BOX->WINDOW
we may send a message directly to the BOX class thus
overriding the immediate parent class:
::__super("BOX"):Display()
or the command ...
::super(BOX):display()
(5) ::__virtual() A METHOD that only returns self, used to create slots
for child methods in abstract classes. To use it just
declare a method as VIRTUAL or DEFERRED. You don't
have to reference it anywhere else in your source code.
It will not be inherited in any child classes. That is
your job ;)
Note: A virtual METHOD would also be created this way:
...
METHOD TestVirtual INLINE self
...
This approach is actually faster but this method will
be inherited in child classes as is...
(6) ::classInfo() This couldn't be missing <g>. Everybody seems to be
including one of these in their libraries, so why
not... This is a class inspection utility. You can
not alter anything in the class so far but it provides
good info and cleans up after itself.
(7) ::nMessage() This is used for assignment and retrieval of values to
class instance variables, directly using self.
The class dictionary is scanned for the text described
in the first parameter. If found, it either assigns a
new value to it or returns its current value:
self:nMessage( <cMsg> [, <xValue>] )
You must #define NO_DIRECT_IVARS to use this approach.
See the section below "Direct modification of Instance
Variables (early binding)" for a description of a much
faster approach. This internal method is only included here for completion and should REALLY be written in C.
However, when using code written for Class(y) it makes
it easier to compile without too many changes.
(8) ::InsData() This is the most useful internal METHOD so far. It is
used to add new IVars, on the fly, at runtime. For
example you can add IVars to access database fields
AFTER class initialization e.g.
(Be careful not to run out of IVar functions. If this
happens, add some IVar functions to bsg.prg)
o:InsData( <cIVarName>, [<bBlock>], [<xData>] )
Arguments :
<cIVarName> is the name (message) of the IVar
<bBlock> is an optional code block (same as DATA ... BLOCK)
<cData> is optional value of the IVar, not used if <bBlock>
is used.
// This example shows how database field variables can be inserted
// at runtime. Read oDBTest1.prg and oDBTest2.prg for two __different__
// approaches to the same problem.
CLASS dBServer ALIAS dBS
DATA aBuffer AS ARRAY // MUST be #1
DATA Alias AS ""
...
...
METHOD AddFlds = _AddFlds // Initializes field IVars
METHOD Read = dbsRead // Reads record into ::aBuffer
METHOD Write = dbsWrite // Writes record from ::aBuffer
...
...
ENDCLASS
...
...
METHOD FUNCTION dbsWrite()
AEval( ::Buffer, { | u, n | (::cAlias)->(FieldPut( n, u )) } )
(::cAlias)->(DbSkip(0))
(::cAlias)->(dbCommit())
RETURN self
METHOD FUNCTION dbsRead()
Local i
FOR i=1 TO LEN( ::Buffer )
::Buffer[ i ] := (::cAlias)->(FieldGet( i ))
NEXT
RETURN self
METHOD FUNCTION _AddFlds()
// After you you have the database open and ready
FOR i=1 TO (::alias)->(FCOUNT())
// get set block for ::aBuffer array
::InsData( Field( i ), &( "{ | Self, xVal | if( xVal == NIL, Self[ 1 ][" +;
Str( i ) + "], Self[ 1 ][" + Str( i ) + "] := xVal ) }" ) )
NEXT
RETURN self
DIRECT MODIFICATION OF INSTANCE VARIABLES (Early Binding):
----------------------------------------------------------
Early binding of IVars is not yet implemented. However there is an easy way to do it with any class library that does not modify the order in which
IVars were declared during class creation.
What you do is you directly modify the self array element that the IVar
occupies. This is much faster than the usual way of IVar addressing (::)
and it is also the only way to alter the value of a READONLY IVar, e.g.
#xTranslate @:Color => self\[\1] // The Class(y) way
CLASS window ALIAS win
DATA Color // Declare ::Color FIRST as Self[1]
...
ENDCLASS
...
...
@:Color := "N/W"
@:Color := "R/W"
However this is only valid in the actual class itself and NOT in ANY inherited classes ( except if you use ::super(SUPERCLASS):IVar )
Another case to use this syntax is from within User Defined IVar Functions.
In these cases you CAN NOT use the :: operator as it would result in an
internal error (processor stack fault) and seven years of bad luck :-)
( see also LINKed IVars below and take a look at ClipOO.ngt in the
HELP directory )