Key Elements of Delphi Class Definitions - ablealias/Delphi GitHub Wiki
Unit Structure
Delphi units (.PAS files) allow declaration of interface
and implementation
sections. The interface defines the part that is visible to other units using that unit. The keyword uses
can be added to a unit's interface or implementation section to list the other units that your unit uses. This indicates to the compiler that your unit refers to parts of the used unit's interface. Parts of a unit declared in the implementation section are all private to that unit
, i.e. never visible to any other unit. Types, functions and procedures declared in the interface of a unit must have a corresponding implementation, or be declared as external (e.g. a call to a function in a DLL).
Class Interfaces
Classes are defined as types
in Delphi and may contain fields of standard data types or other objects, methods declared as functions or procedures, and properties
. The type declaration of a class defines its interface and the scope of access to fields, methods and properties of the class. Class interfaces are usually defined in the interface of a unit to make them accessible to other modules using that unit. However they don't need to be. Sometimes a type declaration of a class may be used only within the implementation part of a unit.
Properties
Properties are a specialised interface to a field of a defined type, allowing access control through read and write methods. Properties are not virtual
, you can replace a property with another property of the same name, but the parent class doesn't know about the new property. It is however possible to make the access methods (read/write methods) of a property virtual
.
Inheritance
Delphi's inheritance model is based on a single hierarchy
. Every class inherits from TObject
and can have only one parent
. A descendant class inherits all of the interface and functionality of its parent class, subject to the scope described below.
`Multiple inheritance from more than one parent is not allowed directly. It can be implemented by using a container class to create instances one or more other classes and selectively expose parts of the contained classes.
Private, Protected, Public and Published Scope
Scope refers to the visibility of methods
and data defined in the interface of a class, i.e. what parts of the class are accessible to the rest of the application or to descendant classes.
The default scope is public
, for instance the component instances you add to a form at design time. Public says "come and get me"; it makes the data or method visible to everything at runtime.
Published parts of a class are a specialized form of Public scope. They indicate special behavior for classes derived from TPersistent. A persistent class can save and restore its published properties to persistent storage using Delphi's standard streaming methods
. Published properties also interact with Delphi Object Inspector
in the IDE. A class must descend from TPersistent in order to use Published. There's also not much point in publishing methods, since you can't store them, although Delphi's compiler doesn't stop you. Published also lets another application access details of the class through Delphi's runtime type information.
This would be rarely used, except in Delphi's design time interaction with its VCL.
Encapsulation or information hiding is essential to object orientation, so Protected
and Private
scope let you narrow the access to parts of a class.
Protected parts are visible only to descendant classes, or to other classes defined in the same unit.
Private parts are visible only to the defining class, or to other classes defined in the same unit.
It's important to note that once something is given public or published scope, it cannot be hidden in descendant classes.
Static, Virtual and Dynamic Methods; Override and Inherited
Methods declared as virtual
or dynamic
let you change their behavior using override in a descendant class.
You're unlikely to see a virtual method in the private part of a class, since it could only be overridden in the same unit, although Delphi's compiler doesn't stop you from doing this.
Override indicates that your new method replaces the method of the same name from the parent class. The override must be declared with the same name and parameters as the original method.
When a method is overridden, a call to the parent class's method actually executes the override method in the real class of the object.
Static methods on the other hand have no virtual or override declaration. You can replace a method of a class in a descendant class by redeclaring another method, however this is not object oriented. If you reference your descendant class as the parent type and try to call the replaced method, the static method of the parent class is executed. So in most cases, it's a bad idea to replace a static method.
Virtual and dynamic methods can be used interchangeably. They differ only in their treatment by the compiler and runtime library. Delphi's help explains that dynamic methods have their implementation resolved at compile time and run slightly faster, whereas virtual methods are resolved at runtime, resulting in slightly slower access but a smaller compiled program.
Virtual is usually the preferred declaration. Delphi's help suggests using dynamic when you have a base class with many descendants that may not override the method.
The inherited
directive lets you refer back to a property or method as it was declared in the parent class. This is most often used in the implementation of an override method, to call the inherited method of the parent class and then supplement its behavior.
Abstract Methods
Abstract is used in base classes to declare a method in the interface and defer its implementation to a descendant class. I.e. it defines an interface, but not the underlying operation. Abstract must be used with the virtual or dynamic directive. Abstract methods are never implemented in the base class and must be implemented in descendant classes to be used.
A runtime error occurs if you try to execute an abstract method that is not overridden. Calling inherited within the override implementation of an abstract method will also result in a runtime error, since there is no inherited behavior.
Messages
Delphi's handling of Windows messages is a special case of virtual methods. Message handlers are implemented in classes that descend from TControl. I.e classes that have a handle and can receive messages. Message handlers are always virtual and can be declared in the private part of a class interface, yet still allow the inherited method to be called. Inherited in a message handler just uses the keyword inherited, there is no need to supply the name of the method to call.
Events
Events are also an important characteristic of Delphi, since they let you delegate extensible behaviour to instances of a class. Events are properties that refer to a method of another object. Events are not inherited in Delphi 1; Delphi 2 extends this behaviour to let you use inherited in an event. . Inherited in an event handler just uses the keyword inherited, there is no need to supply the name of the method to call. Events are particularly important to component developers, since they provide a hook for the user of the component to modify its behaviour in a way that may not be foreseen at the time the component is written.
Constructors and Destructors
The constructor and destructor are two special types of methods. The constructor initializes a class instance (allocates memory initialized to 0) and returns a reference (pointer) to the object. The destructor deallocates memory used by the object (but not the memory of other objects created by the object). Classes descended from TObject have a static constructor, Create, and a virtual destructor Destroy. TComponent introduces a new public property, the Owner of the component and this must be initialized in the constructor. TComponent's constructor is declared virtual, i.e. it can be overridden in descendant classes. It is essential when you override a virtual constructor or destructor in a TComponent descendant to include a call to the inherited method.