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
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.
type TCollectionItemClass = class of TCollectionItem; ... TCollection = class(TPersistent) ... constructor Create(ItemClass: TCollectionItemClass);
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 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 runtime, 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';
- Classes and Objects
- Nested Type Declarations
- Operator Overloading (Object Pascal)
- Class and Record Helpers (Object Pascal)