Class References

Class References

The last language feature I'll discuss in this chapter is the use of class references, which implies the idea of manipulating classes themselves within your code. The first point to keep in mind is that a class reference isn't an object; it is a reference to a class type. A class reference type determines the type of a class reference variable. Sounds confusing? A few lines of code will make this concept a little clearer.

Suppose you have defined the class TMyClass. You can now define a new class reference type, related to that class:

  TMyClassRef = class of TMyClass;

Now you can declare variables of both types. The first variable refers to an object, the second to a class:

  AnObject: TMyClass;
  AClassRef: TMyClassRef;
  AnObject := TMyClass.Create;
  AClassRef := TMyClass;

You may wonder what class references are used for. In general, class references allow you to manipulate a class data type at run time. You can use a class reference in any expression where the use of a data type is legal. There are not many such expressions, but the few cases are interesting, like the creation of an object. You can rewrite the last line of the previous code as follows:

AnObject := AClassRef.Create;

This time, you apply the Create constructor to the class reference instead of to an actual class; you use a class reference to create an object of that class.

Class reference types wouldn't be as useful if they didn't support the same type-compatibility rule that applies to class types. When you declare a class reference variable, such as MyClassRef, you can then assign to it that specific class and any inherited class. So if TMyNewClass is an inherited class of TMyClass my class, you can also write

AClassRef := TMyNewClass;

Delphi declares a lot of class references in the run-time library and the VCL, such as the following:

TClass = class of TObject;
TComponentClass = class of TComponent;
TFormClass = class of TForm;

In particular, the TClass class reference type can be used to store a reference to any class you write in Delphi, because every class is ultimately derived from TObject. The TFormClass reference is used in the source code of most Delphi projects. The CreateForm method of the Application object requires as a parameter the class of the form to create:

Application.CreateForm(TForm1, Form1);

The first parameter is a class reference; the second is a variable that stores a reference to the created object instance.

Finally, when you have a class reference, you can apply to it the class methods of the related class. Considering that each class inherits from TObject, you can apply to each class reference some of the methods of TObject, as you'll see in Chapter 3.

Creating Components Using Class References

What is the practical use of class references in Delphi? Being able to manipulate a data type at run time is a fundamental element of the Delphi environment. When you add a new component to a form by selecting it from the Component Palette, you select a data type and create an object of that data type. (Actually, that is what Delphi does for you behind the scenes.) In other words, class references give you polymorphism for object construction.

To give you a better idea of how class references work, I've built an example named ClassRef. The form displayed by this example has three radio buttons, placed inside a panel in the upper portion of the form. When you select one of these radio buttons and click the form, you'll be able to create new components of the three types indicated by the button labels: radio buttons, push buttons, and edit boxes.

To make this program run properly, you need to change the names of the three components. The form must also have a class reference field, declared as ClassRef: TControlClass. It stores a new data type every time the user clicks one of the three radio buttons, with assignments like ClassRef := TEdit. The interesting part of the code is executed when the user clicks the form. Again, I've chosen the OnMouseDown event of the form to hold the position of the mouse click:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
  NewCtrl: TControl;
  MyName: String;
  // create the control
  NewCtrl := ClassRef.Create (Self);
  // hide it temporarily, to avoid flickering
  NewCtrl.Visible := False;
  // set parent and position
  NewCtrl.Parent := Self;
  NewCtrl.Left := X;
  NewCtrl.Top := Y;
  // compute the unique name (and caption)
  Inc (Counter);
  MyName := ClassRef.ClassName + IntToStr (Counter);
  Delete (MyName, 1, 1);
  NewCtrl.Name := MyName;
  // now show it
  NewCtrl.Visible := True;

The first line of the code for this method is the key. It creates a new object of the class data type stored in the ClassRef field. You accomplish this by applying the Create constructor to the class reference. Now you can set the value of the Parent property, set the position of the new component, give the component a name (which is automatically used also as the value of Caption or Text), and make it visible. You can see an example of the output of this program in Figure 2.9.

Click To expand
Figure 2.9: An example of the output of the ClassRef example

For polymorphic construction to work, the base class type of the class reference must have a virtual constructor. If you use a virtual constructor (as in the example), the constructor call applied to the class reference will call the constructor of the type that the class reference variable currently refers to. But without a virtual constructor, your code will call the constructor of fixed class type indicated in the class reference declaration. Virtual constructors are required for polymorphic construction in the same way that virtual methods are required for polymorphism.

Part I: Foundations