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 at times 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:
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 TCollection (in the Classes unit):
type TCollectionItemClass = class of TCollectionItem; ... 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.
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 class's constructor. 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 an object's immediate ancestor class. 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, 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 othera compilation error results. For example,
if ActiveControl is TEdit then TEdit(ActiveControl).SelectAll;
This statement casts a variable to TEdit after first verifying that the object it references is an instance of TEdit or one of its descendants.
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';
Copyright(C) 2009 Embarcadero Technologies, Inc. All Rights Reserved.
What do you think about this topic? Send feedback!