Skip to content

Records & objects

D/BASIC has two answers to “these fields belong together.” Choosing the right one is easier than memorizing either syntax:

A TYPE is a value you can copy. A CLASS is 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.

TYPE NameCard
  Code AS STRING * 8
  Name AS STRING * 32
  Ranking AS INTEGER
END TYPE

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:

DIM Route(0 TO 15) AS Point
Route(0).X = 12
Route(0).Y = 8

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:

  1. CLASS declares the shape and public contract.
  2. Member bodies live at module scope under qualified names such as Greeter.SayHello.
  3. NEW constructs one object and returns a reference.
  4. NOTHING releases the variable's reference.

A declared class variable begins as NOTHING; declaration alone does not run a constructor.

Identity changes assignment

DIM A AS Greeter, B AS Greeter
A = NEW Greeter("MIRA")
B = A

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
  • PUBLIC members are visible to callers.
  • PROTECTED members are visible inside the class and its derived classes.
  • PRIVATE members 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:

FUNCTION Account.CurrentBalance () AS DECIMAL
  CurrentBalance = THIS.Balance
END FUNCTION

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:

Box.Cell(3) = 42
PRINT Box.Cell(3)

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:

CLASS DocumentNode
  PUBLIC:
    Child AS DocumentNode
    WEAK Parent AS DocumentNode
END CLASS

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:

CLASS Ticket
  PUBLIC:
    STATIC Issued AS LONG
END CLASS

Ticket.Issued = Ticket.Issued + 1

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:

SUB ClearSelection (BYREF Selected AS WorksWidget)
  Selected = NOTHING
END SUB

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.


Next: Console I/O & Embedded DATA →