The Gambas Object Model

1. Objects & Classes

An object in Gambas is a data structure that provides properties, variables, methods and events.

Objects are accessed "by reference", i.e. by using a pointer to it, or by using a variable whose value is the address of the object data structure in memory.

You can see the address of an object by using the PRINT instruction:

Dim aStr As New String[]

Print aStr
(String[] 0x80dccf8)

The object data structure is described by a class.

1.1. Classes

Each Gambas object has a class that describes all its public properties, methods and events. This class is a Gambas object too, whose class is the class named Class.

A static class is a class whose all members are static (see below). In Gambas, a static class is also named a module.

A static class cannot be instantiated: it would create an object with no dynamic variable, which is useless.

Example

The System class is a static class: all its methods are static, and you cannot create an object whose class would be System.

A virtual class is an hidden pseudo-class that you cannot explicitly manipulate.

1.2. Properties, methods and variables

Properties and methods allow to manipulate the data structure.

A Property, a Method or a Variable can be static:

A method or a variable can be either public or private. A property is always public.

Private symbols can only be used from the class inside. Public symbols can be used everywhere, provided you have a reference pointing at the object.

1.3. References

There is no garbage collector in Gambas. So each object has a reference counter that is incremented each time the object is referenced by any variable, array, collection or other object, and decremented when it is released.

This reference counter is zero at object creation, and when it becomes zero again after a reference release, the object is freed.

1.4. Invalid objects

An object can become invalid. Because, for example, it is linked to an internal object not managed by Gambas that was destroyed.

Trying to use an invalid object raises an error.

1.5. Special methods

Special methods are methods declared in classes, whose name begins with an underscore character, and that are called by the interpreter in the following situations:

See Special Methods and Special Methods for more information.

2. Events & Observers

2.1. Events

Events are signals sent by an object when something happens on it.

If an object raises events, it will hold a reference on its default observer, or parent object.

This default observer is another object that implements event handlers. An event handler is just a public method that is called each time the event is raised.

By default, the observer is the current object where the newly instantiated object is declared.

To raise events, an object must have an event name. This event name is assigned at object instantiation, when using the NEW instruction and the AS keyword, and is the prefix of all event handler methods.

Example

This creates a Button control that will raise events.

Dim hButton As Button

hButton = New Button(Me) As "ButtonEventName"

Public Sub ButtonEventName_Click()

  Print "This is the 'Click' event handler for the previous button."

End

If no event name is specified, then the object won't raise events.

2.2. Default parent object (or default default observer)

Some objects can have a default parent object if you do not define it by specifying an event name with the As "xxx" syntax.

At the moment, the only objects having a default parent object are forms, which use themselves as default parent object. This was done to mimic the behaviour of Visual Basic™ and help old VB developers.

If you create a Form and specify its event name with the As "xxx" syntax, that default behaviour will be overridden.

2.3. Locking Objects

An object can be locked so that it stops raising events, and can be unlocked so that it raises them again. See the Object.Lock and Object.Unlock methods.

Some events can be cancelled by the event handler, by using the STOP EVENT instruction. The effect of this cancellation depends on the event.

An object is automatically locked during the execution of its constructor: he cannot send nor receive any events.

2.4. Observers

Observers are objects that allow you to intercept events raised by other objects. They "observe" them.

You can intercept events just before they have been raised, or just after.

For each intercepted event, the observer will raise an event with the same name and the same arguments. These events are raised in addition of the original event handled by the parent object.

If you use STOP EVENT inside an observer event handler, the original event is cancelled.

Example

Private $hButton As Button
Private $hObserver As Observer

Public Sub Form_Load()

  $hButton = New Button(Me) As "Button"
  $hObserver = New Observer(hButton) As "Observer"

End

Public Sub Observer_Click()

  Debug "The button has been clicked. I cancel the event!"
  Stop Event

End
 
Public Sub Button_Click()

  Debug "If you see me this is a bug!"

End

3. Inheritance

Inheritance is the way for a class to become a specialized version of another class.

3.1. What is inherited?

The class inherits from its parent every method, property, constant and event.

You must use the ME keyword to access the inherited elements from the class implemantation code.

3.2. Which class can be a parent class?

You can inherit any class, even a native one!

For example, you can create a custom MyListBox class that inherits ListBox but allows to associate a tag with each list item.

Note that you cannot use INHERITS in a form class file, because forms already inherit the Form class.

The inheritance tree depth cannot be greater than 16. This is a constant hard-coded inside the Gambas interpreter.

3.3. Virtual dispatching

When calling a method or accessing a property from an object reference, Gambas always uses virtual dispatching.

It means that the real class of the object is always used, and not the type of the variable that references the object - as it was in Gambas 1.0.

3.4. Inheritance and constructor

Contrary to all the object language I know, each class in the inheritance hierarchy consumes the parameters passed to the constructor.

Let's suppose we have the following inheritance tree:

MyListBox ---inherits--> ListBox ---inherits---> Control

So NEW MyListBox will take two parameters.

Be careful: the ListBox._new() will be called first, so that you are sure that the ListBox control exists when you are in MyListBox._new().

Then you will create a MyListBox control this way:

hMyListBox = New MyListBox(hContainer, "Name")

So the order of arguments is the following:

In Gambas 2.0, the order or arguments were reversed!

For example, if you have the following inheritance:

MyForm --> Form --> Window

with the MyForm constructor being:

Sub _new(FirstArg As String, SecondArg as Integer, Optional ThirdArg As Integer)

Note: the Form constructor takes no argument, and the Window constructor takes an optional parent argument.

The signature of the final constructor will be:

New MyForm(FirstArg As String, SecondArg As Integer, Optional Parent As Control, Optional ThirdArg As Integer)

In a more general way, the order of arguments for a three level inheritance tree is:

3.5. Symbol Overriding

When a symbol is overridden, the signature of the symbol in the child class must be the same as the signature of the symbol in the parent class.

The rules are:

4. Components

Gambas component are external shared libraries written in C, C++ or in Gambas that add new functions and/or classes to the Gambas interpreter.

Classes are grouped according to the component they come from.

4.1. Default internal component

The interpreter includes an internal component named gb that defines all standard classes of the language.

This component is always loaded by default, and can be considered as part of the language.

4.2. Symbol tables

Each component has its own private class symbol table, so that class names do not conflict.

4.3. Global symbol table

So that components can work together, there is a global symbol table, where all classes exported by components and all classes exported by the current project are stored.

If there is a name conflict in this global symbol table, the last loaded class overrides the previous loaded class with the same name, by using inheritance. In other words, the overriding class extends the overridden one.

This last feature can be used for:

4.4. Project symbol table

Your project has its own private symbol, like any component, and can export any of its classes to the global symbol table by using the EXPORT keyword.

The project classes are loaded after all components. So your exported class can override any exported classes declared in any component.

See also