Icon Methods

Methods are simply functions and procedures that are declared as part of a class declaration. They are declared in the same way as functions and procedures that are declared in a unit. Please see the Function and Procedure Declarations topic for more information on the proper syntax. However, methods offer three additional keywords: virtual, abstract, and override.

Virtual Methods
The virtual keyword allows you to specify whether or not a method can be overridden by descendant classes. Virtual methods form the basis of inheritance in an object-oriented language because they allow the developer to supplement or replace existing functionality in an ancestor class with functionality that is more specific to the current class. For example, consider the following example class declarations and implementations:

interface

   TVehicle = class
      protected
         function GetNumWheels: Integer; virtual;
      public
         property NumWheels: Integer read GetNumWheels;
      end;

   TTruck = class(TVehicle)
      protected
         function GetNumWheels: Integer; override;
      end;

implementation

function TVehicle.GetNumWheels: Integer;
begin
   Result := 4;
end;

function TTruck.GetNumWheels: Integer;
begin
   Result := 10;
end;

As you can see, the default value returned from the GetNumWheels method is 4. But, because the GetNumWheels method is virtual, descendant classes like the TTruck class can override the method to provide a different result that is accurate for the type of vehicle being represented by the class.

Information A virtual method does not have to be present in the immediate ancestor class in order for it to be overridden. You can override any virtual method that exists in any ancestor class, no matter how far removed it is from the class being declared. Also, once a method is declared as virtual, it is always virtual and capable of being overridden by a descendant class.

In addition, you can use the abstract keyword to specify that the virtual method isn't actually implemented in the current class, but rather must be implemented by descendant classes. Using the above example, the base TVehicle class could be declared and implemented as follows instead:

interface

   TVehicle = class
      protected
         function GetNumWheels: Integer; virtual; abstract;
      public
         property NumWheels: Integer read GetNumWheels;
      end;

   TTruck = class(TVehicle)
      protected
         function GetNumWheels: Integer; override;
      end;

implementation

function TTruck.GetNumWheels: Integer;
begin
   Result := 10;
end;

Information Any classes that contain abstract methods cannot be directly created. Any attempt to do so will cause a run-time error.

If you want to augment, or add to, the functionality present in the virtual method of an ancestor class, then you can use the inherited keyword in the implementation of the descendant class method to do so. For example, in this class hierarchy the TCar class defines a GetAvailableColors method that returns a comma-delimited list of the default colors that a car is available in for the car manufacturer. The descendant TJaguar class that represents a sports car overrides the GetAvailableColors method to specify additional colors that the sports car is available in:

interface

   TCar = class
      protected
         function GetAvailableColors: String; virtual;
      public
         property AvailableColors: String read GetAvailableColors;
      end;

   TJaguar = class(TCar)
      protected
         function GetAvailableColors: String; override;
      end;

implementation

function TCar.GetAvailableColors: String;
begin
   Result := 'Red, Black, White';
end;

function TJaguar.GetAvailableColors: String;
begin
   Result := inherited GetAvailableColors + ', Silver';
end;

Overloaded Methods
Overloaded methods are methods that have more than one declaration in a class and each declaration has the same name but different parameters. This is very useful for situations where a method may need to be called using different parameters. For example, consider the following class declaration:

TCustomers = class
   public
      procedure Delete(ID: Integer);
      procedure Delete(const Name: String);
   end;

In this example, the TCustomers class has overloaded the Delete method so that it can be called with either an integer customer ID or a string customer name.

Although default parameters are usually easier, overloaded methods can also be used to implement optional parameters. For example, the following class allows its Add method to be called with an ID, a name, or both:

TCustomers = class
   public
      procedure Add(ID: Integer);
      procedure Add(ID: Integer; const Name: String);
      procedure Add(const Name: String);
   end;

Information Other variants of Object Pascal require that you use the overload keyword to indicate that a method is overloaded. Elevate Web Builder does not use the overloaded keyword because it is unnecessary. The compiler knows if two methods have the same declaration, and will issue an error if they do. The compiler also knows how to find the proper method declaration, or whether one exists at all, by how the method is called. Finally, if an overloaded method has one or more declared versions that aren't actually called, the compiler knows this and will not emit the method during compilation.

Class Methods
Class methods are special types of methods that are sometimes referred to as "static" methods. They are callable from class instances and also directly from class references where no instance of the class exists, and are useful for implementing functions and procedures that should be encapsulated within the context of a class, but don't need to access class instance variables, properties, or methods. Class methods can, however, access any class variables or class properties that are also declared in the same class. Please see the Variables and Properties topics for more information on class variables and properties.

Class methods are declared by prefacing a method declaration with the class keyword. For example, the following class declaration includes a class variable that keeps track of how many instances of the class have been created along with a class method that returns the creation count:

TMyClass = class
   private
      class FCreateCount: Integer;
   public
      constructor Create; override;
      class function GetCreateCount: Integer;
   end;

implementation

constructor TMyClass.Create;
begin
   inherited Create;
   Inc(FCreateCount);
end;

The GetCreateCount class method above could be accessed in two different ways. The first way is by referring to the GetCreateCount class method for an instance of the TMyClass class:

procedure ShowCreateCount;
var
   TempInstance: TMyClass;
begin
   TempInstance:=TMyClass.Create;
   try
      ShowMessage(IntToStr(TempInstance.GetCreateCount));
   finally
      TempInstance.Free;
   end;
end;

The second way is by using a direct class reference:

procedure ShowCreateCount;
begin
   ShowMessage(IntToStr(TMyClass.GetCreateCount));
end;

The second way is easiest when you don't have an instance of the class available.

Class methods are very useful for implementing factory classes that create instances, implementing singleton instances of classes, and implementing namespaces for code that otherwise would use normal functions and procedures declared outside of a class.

Constructors and Destructors
Constructors and destructors are special methods that handle the process of creating a class instance and destroying it. These methods are crucial to ensuring that resources are properly allocated and initialized during the creation of class instances, and that resources are properly disposed of during the destruction of class instances. The base TObject class declaration contains both a constructor called Create and a destructor called Destroy.

Information Constructors and destructors are completely optional. If a class declaration doesn't contain a constructor and/or destructor, then the ancestor class's constructor and/or destructor is used instead. If the ancestor class doesn't contain a constructor and/or destructor, then it's ancestor class is used and so on, until the base TObject class is reached by the compiler.

Constructors

Constructors are declared by prefacing a method declaration with the keyword constructor. Constructors must be declared as a procedure called Create with no result type declaration due to the fact the result is implicitly an instance of the class in which the declaration exists. Trying to declare a constructor with a different name or with a result type will cause a compiler error. If the declared constructor does not accept any parameters, then it must also be declared as an override of the base TObject Create constructor. Constructors can be overloaded, so it is possible to declare different constructors with different parameters.

Warning Do not call constructors on instances of classes. Constructors are class, or static, methods, and should only be called on a class type itself in order to create an instance of the class.

The following is an example of a class that declares both an override of the base TObject Create constructor, as well as creates its own overloaded constructor:

interface

   TCustomer = class
      private
         FID: Integer;
         FName: String;
      public
         property ID: Integer read FID write FID;
         property Name: String read FName write FName;
      end;

   TCustomers = class
      private
         FCustomers: TObjectList;  // TObjectList class is declared
                                   // in the WebCore unit
         procedure CreateDemoCustomers(Value: Integer);
         function GetNumCustomers: Integer;
         function GetCustomer(ID: Integer): TCustomer;
         function GetCustomer(const Name: String): TCustomer;
      public
         constructor Create; override;
         constructor Create(NumDemoCustomers: Integer);
         property NumCustomers: Integer read GetNumCustomers;
         property Customer[ID: Integer]: TCustomer read GetCustomer; default;
         property Customer[const Name: String]: TCustomer read GetCustomer; default;
      end;

implementation

constructor TCustomers.Create;
begin
   inherited Create;
   FCustomers:=TObjectList.Create;
end;

constructor TCustomers.Create(NumDemoCustomers: Integer);
begin
   Create;
   CreateCustomers(NumDemoCustomers);
end;

procedure TCustomers.CreateDemoCustomers(Value: Integer);
var
   I: Integer;
   TempCustomer: TCustomer;
begin
   for I:=0 to Value-1 do
      begin
      TempCustomer:=TCustomer.Create;
      with TempCustomer do
         begin
         ID:=I;
         Name:='Demo Customer #'+IntToStr(I);
         end;
      FCustomers.Add(TempCustomer);
      end;
end;

function TCustomers.GetNumCustomers: Integer;
begin
   Result:=FCustomers.Count;
end;

function TCustomers.GetCustomer(ID: Integer): TCustomer;
var
   I: Integer;
begin
   Result:=nil;
   for I:=0 to FCustomers.Count-1 do
      begin
      if (TCustomer(FCustomers[I]).ID=ID) then
         begin
         Result:=TCustomer(FCustomers[I]);
         Break;
         end;
      end;
end;

function TCustomers.GetCustomer(const Name: String): TCustomer;
var
   I: Integer;
begin
   Result:=nil;
   for I:=0 to FCustomers.Count-1 do
      begin
      if SameStr(TCustomer(FCustomers[I]).Name,Name) then
         begin
         Result:=TCustomer(FCustomers[I]);
         Break;
         end;
      end;
end;

Information The above class declaration includes default array properties. Please see the Properties topic for more information on default array properties.

Destructors

A destructor is declared by prefacing a method declaration with the keyword destructor. There can be only one destructor per class and it must be declared as a procedure called Destroy that overrides the base TObject Destroy destructor and has no parameters. Trying to declare a destructor with a different name or with parameters will cause a compiler error. Finally, the destructor must be declared in the public scope of the class declaration and cannot be declared in any other scope.

The above example for constructors is an example of a class with an overridden Destroy destructor.

Free Method

Do not call the Destroy method above directly, use the TObject Free method instead. The Free method performs an extra crucial step that the Destroy method does not: the Free method checks to see if the calling instance variable is already nil, and only calls the Destroy method if the instance variable is not nil.

Information The only exception to the above rule is when calling the inherited Destroy method from within an ancestor class's Destroy method. That is a perfectly valid way to call the Destroy method directly.

Self

Use the special Self keyword in order to reference the current class instance from within a method. This is useful for situations where local variable or parameter names may conflict with a specific variable, method, or property name of the class in which the method resides, and so those identifiers need to be prefixed with the Self keyword.
Image