Records & objects¶
D/BASIC has two answers to “these fields belong together.” Choosing the right one is easier than memorizing either syntax:
A
TYPEis a value you can copy. ACLASSis an object with identity.
TYPE: a value record¶
TYPE Point
X AS INTEGER
Y AS INTEGER
END TYPE
DIM A AS Point, B AS Point
A.X = 3
A.Y = 4
B = A
B.X = 99
PRINT A.X ' 3: B is an independent copy
A record lives inline. Assignment copies its fields. It has no constructor, destructor, identity, virtual table, or reference count.
Use a record for coordinates, colors, headers, small messages, dates, and other data that should travel as a self-contained value.
Record fields¶
Fields form a closed value graph: numeric scalars, fixed strings, and nested records. A record field cannot be an array, a variable-length STRING, a CLASS reference, a handle/resource, or a WEAK link. Recursive and cyclic record layouts are refused as well.
STRING * n is fixed-width record storage; n is from 1 through 65,535. Assignment truncates input longer than n and space-pads input shorter than n; every stored byte is significant. The rule is deliberately exact:
DIM Card AS NameCard
Card.Name = "Alex"
PRINT LEN(Card.Name) ' 32
PRINT Card.Name = "Alex" ' 0: the padded field is not four bytes
PRINT RTRIM$(Card.Name) = "Alex" ' -1
An ordinary variable-length STRING keeps only its logical bytes behind a counted reference, which is why it cannot live inside a TYPE value image. Use a CLASS when a model needs variable strings or reference-shaped state. The fixed-string rule is useful for portable record images, but it is also a compatibility trap worth learning once.
Arrays of records are ordinary value arrays:
CLASS: identity and behavior¶
Here is the first object example to study end to end:
CLASS Greeter
PRIVATE:
Name AS STRING
PUBLIC:
DECLARE CONSTRUCTOR (Initial AS STRING)
DECLARE SUB SayHello ()
END CLASS
CONSTRUCTOR Greeter (Initial AS STRING)
THIS.Name = Initial
END CONSTRUCTOR
SUB Greeter.SayHello ()
PRINT "HELLO, "; THIS.Name; "!"
END SUB
DIM G AS Greeter
G = NEW Greeter("ADA")
G.SayHello
G = NOTHING
Read it in four passes:
CLASSdeclares the shape and public contract.- Member bodies live at module scope under qualified names such as
Greeter.SayHello. NEWconstructs one object and returns a reference.NOTHINGreleases the variable's reference.
A declared class variable begins as NOTHING; declaration alone does not run a constructor.
Identity changes assignment¶
A and B now refer to the same object. Mutating it through either name is visible through the other. Use A IS B to test identity and A IS NOTHING to test the empty reference.
SET A = B is accepted for familiar object-oriented BASIC style; ordinary A = B has the same reference-assignment meaning when the destination is a class type.
Access sections¶
CLASS Account
PRIVATE:
Balance AS DECIMAL
PROTECTED:
Revision AS LONG
PUBLIC:
DECLARE FUNCTION CurrentBalance () AS DECIMAL
END CLASS
PUBLICmembers are visible to callers.PROTECTEDmembers are visible inside the class and its derived classes.PRIVATEmembers are visible only inside the declaring class.
Access is part of the compiled class contract, not an editor convention.
THIS¶
Inside a member body, THIS is the current receiver:
Qualifying fields with THIS makes it clear which state belongs to the object, especially when a parameter has a similar name.
Constructors and destructors¶
A class can declare constructor overloads and one parameterless destructor:
CLASS Note
PUBLIC:
Text AS STRING
DECLARE CONSTRUCTOR ()
DECLARE CONSTRUCTOR (Initial AS STRING)
DECLARE DESTRUCTOR
END CLASS
CONSTRUCTOR Note ()
THIS.Text = "UNTITLED"
END CONSTRUCTOR
CONSTRUCTOR Note (Initial AS STRING)
THIS.Text = Initial
END CONSTRUCTOR
DESTRUCTOR Note
' Release application-owned state here.
END DESTRUCTOR
Objects use deterministic reference counting. When the final strong reference is released, the destructor runs. There is no stop-the-world garbage collector.
Construction proceeds base first, then derived. Destruction proceeds derived first, then base.
Inheritance and BASE¶
CLASS NamedThing
PROTECTED:
Name AS STRING
PUBLIC:
DECLARE CONSTRUCTOR (Value AS STRING)
DECLARE VIRTUAL FUNCTION Description$ ()
END CLASS
CLASS ButtonThing EXTENDS NamedThing
PUBLIC:
DECLARE CONSTRUCTOR (Value AS STRING)
DECLARE FUNCTION Description$ () OVERRIDE
END CLASS
CONSTRUCTOR NamedThing (Value AS STRING)
THIS.Name = Value
END CONSTRUCTOR
CONSTRUCTOR ButtonThing (Value AS STRING)
BASE(Value)
END CONSTRUCTOR
FUNCTION NamedThing.Description$ ()
Description$ = THIS.Name
END FUNCTION
FUNCTION ButtonThing.Description$ ()
Description$ = "BUTTON: " + THIS.Name
END FUNCTION
D/BASIC uses single inheritance. BASE(args) explicitly chooses the base constructor; omit it to use the base class's zero-argument constructor.
Virtual and abstract methods¶
VIRTUAL establishes a dispatch slot. A derived implementation must say OVERRIDE, making an accidental spelling mismatch a diagnostic instead of a new method.
DIM Item AS NamedThing
Item = NEW ButtonThing("SAVE")
PRINT Item.Description$() ' dispatches to ButtonThing
ABSTRACT declares a required virtual operation with no base implementation. An abstract class supplies a contract and can never itself be instantiated; instantiate a concrete derived class that implements every required operation.
Properties¶
A property makes a read or write look like member access while keeping behavior in explicit accessor bodies:
CLASS Meter
PRIVATE:
Stored AS INTEGER
PUBLIC:
DECLARE PROPERTY Value () AS INTEGER
DECLARE PROPERTY Value (NewValue AS INTEGER)
END CLASS
PROPERTY Meter.Value () AS INTEGER
Value = THIS.Stored
END PROPERTY
PROPERTY Meter.Value (NewValue AS INTEGER)
THIS.Stored = NewValue
END PROPERTY
DIM M AS Meter
M = NEW Meter()
M.Value = 12
PRINT M.Value
Indexed properties place index parameters before the final setter value:
Property += evaluates the receiver, getter, right-hand side, and setter exactly once in that order.
Strong and weak relationships¶
Ordinary object fields are strong references: they keep the target alive. A WEAK field observes without retaining:
This is the classic tree pattern. Parents strongly own children; children weakly point back. After a weak target dies, reading the field produces NOTHING rather than a dangling reference.
Class fields may contain numeric values, dynamic strings, inline TYPE records, and strong or weak object references. They may not directly contain arrays or host resource/handle values. Put a collection behind an object API, and let a D/Works adapter object own any host-side resource lifetime rather than smuggling a native handle into application state.
Static fields¶
A STATIC class field belongs to the class rather than any one instance:
Use static state sparingly. Instance state is easier to test, compose, and run in more than one window.
Objects as parameters¶
BYVAL receives a retained alias and cannot rebind the caller. BYREF can change which object the caller's variable names:
Mutating the object itself is visible through every alias either way. The BYVAL/BYREF distinction controls rebinding of the variable, not cloning of the object.
The rule-of-thumb table¶
| Question | Choose TYPE |
Choose CLASS |
|---|---|---|
| Should assignment make an independent copy? | ✓ | |
| Does it need constructors or methods? | ✓ | |
| Does identity matter? | ✓ | |
| Is it a compact transferable data shape? | ✓ | |
| Does it own other objects or coordinate an adapter lifetime? | ✓ | |
| Does it need inheritance or virtual dispatch? | ✓ |
When in doubt, begin with a TYPE. Promote the model to a CLASS when identity or behavior becomes part of the problem—not merely because objects sound more modern.