Class References

From RAD Studio
Jump to: navigation, search

Go Up to Classes and Objects Index

Sometimes operations are performed on a class itself, rather than on instances of a class (that is, objects). This happens, for example, when you call a constructor method using a class reference. You can always refer to a specific class using its name, but sometimes it is necessary to declare variables or parameters that take classes as values, and in these situations you need class-reference types.

This topic covers the following material:

  • Class reference types
  • Class operators

Class-Reference Types

A class-reference type, sometimes called a metaclass, is denoted by a construction of the form:

class of type

where type is any class type. The identifier type itself denotes a value whose type is class of type. If type1 is an ancestor of type2, then class of type2 is assignment-compatible with class of type1. Thus:

 type TClass = class of TObject;
 var AnyObj: TClass;

declares a variable called AnyObj that can hold a reference to any class. (The definition of a class-reference type cannot occur directly in a variable declaration or parameter list.) You can assign the value nil to a variable of any class-reference type.

To see how class-reference types are used, look at the declaration of the constructor for System.Classes.TCollection (in the System.Classes unit):

 
 type TCollectionItemClass = class of TCollectionItem;
      ...
 TCollection = class(TPersistent)
      ...
   constructor Create(ItemClass: TCollectionItemClass);

This declaration says that to create a TCollection instance object, you must pass to the constructor the name of a class descending from TCollectionItem.

Class-reference types are useful when you want to invoke a class method or virtual constructor on a class or object whose actual type is unknown at compile time.

Constructors and Class References

A constructor can be called using a variable of a class-reference type. This allows construction of objects whose type isn't known at compile time. For example:

 
 type TControlClass = class of TControl;
 
 function CreateControl(ControlClass: TControlClass;
     const ControlName: string; X, Y, W, H: Integer): TControl;
   begin
     Result := ControlClass.Create(MainForm);
     with Result do
       begin
         Parent := MainForm;
         Name := ControlName;
         SetBounds(X, Y, W, H);
         Visible := True;
       end;
   end;

The CreateControl function requires a class-reference parameter to tell it what kind of control to create. It uses this parameter to call the constructor of the class. Because class-type identifiers denote class-reference values, a call to CreateControl can specify the identifier of the class to create an instance of. For example:

 CreateControl(TEdit, 'Edit1', 10, 10, 100, 20);

Constructors called using class references are usually virtual. The constructor implementation activated by the call depends on the runtime type of the class reference.

Class Operators

Class methods operate on class references. Every class inherits two class methods from TObject, called ClassType and ClassParent. These methods return, respectively, a reference to the class of an object and to the immediate ancestor class of an object. Both methods return a value of type TClass (where TClass = class of TObject), which can be cast to a more specific type. Every class also inherits a method called InheritsFrom that tests whether the object where it is called descends from a specified class. These methods are used by the is and as operators, and it is seldom necessary to call them directly.

The is Operator

The is operator, which performs dynamic type checking, is used to verify the actual runtime class of an object. The expression:

object is class

returns True if object is an instance of the class denoted by class or one of its descendants, and False otherwise. (If object is nil, the result is False.) If the declared type of object is unrelated to class -- that is, if the types are distinct and one is not an ancestor of the other -- a compilation error results. For example:

 if ActiveControl is TEdit then TEdit(ActiveControl).SelectAll;

This statement casts the ActiveControl variable to the TEdit type. First it verifies that the object referenced by ActiveControl is an instance of TEdit or one of its descendants.

The as Operator

The as operator performs checked typecasts. The expression

object as class

returns a reference to the same object as object, but with the type given by class. At run time, object must be an instance of the class denoted by class or one of its descendants, or be nil; otherwise an exception is raised. If the declared type of object is unrelated to class - that is, if the types are distinct and one is not an ancestor of the other - a compilation error results. For example:

 
 with Sender as TButton do
  begin
   Caption := '&Ok';
   OnClick := OkClick;
  end;

The rules of operator precedence often require as typecasts to be enclosed in parentheses. For example:

 (Sender as TButton).Caption := '&Ok';

See Also

Code Examples