Veskrze každý zná základní třídu TObject a její metody jako Free, constructor Create a destructor Destroy. Asi znáte i užitečnou class function ClassName, která vrací název třídy a sem tam šikovné metody ClassType a ClassParent, které vrací TClass, tj. referenci na třídu za běhu.
Rád bych upozornil na některé zajímavé elementy deklarace TObject. Níže je deklarace třídy tak, jak je v Delphi 2010.
Modří již vědí, že v Delphi 2009 došlo k rozšíření o čtyři kousky a to o class function UnitName a tři virtuální metody - Equals, GetHashCode a ToString - velmi užitečné a navíc prudce zvyšující kompatibilitu s Delphi Prism a .NET.
Delphi 2010 jen navíc určuje viditelnost na public (asi vzhledem k novému RTTI) a ClassInfo je inline.
Deklarace TObject
TObject = class
public
constructor Create;
procedure Free;
class function InitInstance(Instance: Pointer): TObject;
procedure CleanupInstance;
function ClassType: TClass; inline;
class function ClassName: string;
class function ClassNameIs(const Name: string): Boolean;
class function ClassParent: TClass;
class function ClassInfo: Pointer; inline;
class function InstanceSize: Longint; inline;
class function InheritsFrom(AClass: TClass): Boolean;
class function MethodAddress(const Name: ShortString): Pointer; overload;
class function MethodAddress(const Name: string): Pointer; overload;
class function MethodName(Address: Pointer): string;
function FieldAddress(const Name: ShortString): Pointer; overload;
function FieldAddress(const Name: string): Pointer; overload;
function GetInterface(const IID: TGUID; out Obj): Boolean;
class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
class function GetInterfaceTable: PInterfaceTable;
class function UnitName: string;
function Equals(Obj: TObject): Boolean; virtual;
function GetHashCode: Integer; virtual;
function ToString: string; virtual;
function SafeCallException(ExceptObject: TObject;
ExceptAddr: Pointer): HResult; virtual;
procedure AfterConstruction; virtual;
procedure BeforeDestruction; virtual;
procedure Dispatch(var Message); virtual;
procedure DefaultHandler(var Message); virtual;
class function NewInstance: TObject; virtual;
procedure FreeInstance; virtual;
destructor Destroy; virtual;
end;
Jak jsem řekl, ClassType je málo využívaná, většinou si člověk vystačí s is a as, ale např. můžete mít za běhu seznam tříd (ne objektů) a v kombinaci s ClassName vybrat požadovanou třídu a její referenci získanou právě přes ClassType předat dále ke zpracování atd.
ClassParent jsem použil např. tak, že jsem měl definován seznam názvů tříd formulářů a k nim definovanou externě ikonu. A při požadavku na vznik některého následníka, jsem našel poslední definovanou ikonu v hierarchii požadované třídy. Ale určitě Vás napadne i jiné použití.
ClassInfo vrací ukazatal na RTTI. Pro pěknou práci s RTTI slouží jednotka typinfo a už tu RTTI bylo.
InstanceSize vrací překvapivě velikost instance. writeln(TObject.InstanceSize); vrátí 8 byte.
TObject.AfterConstruction a TObject.BeforeDestruction
AfterConstruction a BeforeDestruction jsou volány v patřičných okamžicích, ve VCL jsou používány např. na vytvoření TForm.OnCreate. Podle helpu BeforeDestruction není volán, pokud došlo v konstruktoru k výjimce.
type
TDemoObject = class (TObject)
public
constructor Create;
destructor Destroy; override;
procedure AfterConstruction; override; // po posledním konstruktoru
procedure BeforeDestruction; override;
end;
{ TDemoObject }
procedure TDemoObject.AfterConstruction;
begin
writeln('AfterConstruction');
end;
procedure TDemoObject.BeforeDestruction;
begin
writeln('BeforeDestruction');
end;
constructor TDemoObject.Create;
begin
writeln('Create');
end;
destructor TDemoObject.Destroy;
begin
writeln('Destroy');
inherited;
end;
begin
with TDemoObject.Create do
Free;
end.
Výsledek běhu:
Create
AfterConstruction
BeforeDestruction
Destroy
TObject.UnitName
UnitName by neměl ničím překvapit.
program Muj.Cool.Project.SP2;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TDemoObject = class (TObject);
begin
writeln(TDemoObject.UnitName);
end.
Vypíše: Muj.Cool.Project.SP2, čímž se nenápadně dostáváme k tomu, že názvy jednotek mohou být strukturované.
TObject.Equals
Nová metoda, která vrací zda jsou instance stejné. V základu toho moc neumí, ale jelikož je virtuální tak se otevírají zajímavé možnosti.
function TObject.Equals(Obj: TObject): Boolean;
begin
Result := Obj = Self;
end;
TObject.ToString
Základní implementace volá ClassName tj. název třídy.
function TObject.ToString: string;
begin
Result := ClassName;
end;
Mezi třídy, které ji předefinovávají patří třeba TStringWriter nebo Exception.
TObject.GetHashCode
Opět nová virtuální metodu, která vrací integer s hashcode. Základní implementace:
function TObject.GetHashCode: Integer;
begin
Result := Integer(Self);
end;
Osobně nechápu proč není nějaká jednoduchá funkce na získání hashe stringu ( - už jsem ji našel, viz dále), takže použijte následující kód.
program TestHash;
{$APPTYPE CONSOLE}
uses
Generics.Defaults;
function GetHashCode(const s:AnsiString):Integer;
begin
Result := BobJenkinsHash(s[1], Length(s) * SizeOf(s[1]), 0);
end;
begin
writeln(GetHashCode('delphi.cz'));
end.
No a nebo použijte SysUtils.HashName - writeln(HashName('delphi.cz'));