Interfaces in Delphi für Win32
Bevor ich auf die Interface von .NET eingehe, möchte ich noch einmal kurz die Interfaces erläutern, wie diese unter Delphi für
Win32 durch Borland implementiert wurden.
Alle in Delphi erstellten Interfaces wurden unter Delphi von
IInterface abgeleitet. IInterface ist wie folgend deklariert:
Delphi-Quellcode:
type
IInterface = interface
['{00000000-0000-0000-C000-000000000046}']
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
IUnknown = IInterface;
Uns interessieren jetzt besonders die Methoden
_AddRef und
_Release. Diese wurden von Delphi genutzt, um ein automatisches Referenz-Counting zu implementieren. Kurz heißt das, wann immer ein Objekt (ob TObject- oder IInterface-Abkömmlinge) einer Interface-Variablen zugewiesen wird, hat Delphi intern automatisch den Referenzzählen erhöht (_AddRef). Wenn immer eine Interface-Variable durch eine neues Objekt überschrieben wurde, bzw. mit
nil gelöscht wurde, bzw. der Zugriffsbereich der Variable verlassen wurde, hat Delphi automatisch einen Aufruf zu
_Release eingefügt. Wurde jetzt die letzte Referenz frei gegeben,
_Release liefert den Wert
0 zurück, hat die Objekt-Implementierung von _Release das Objekt selbständig freigegeben. Das Standardbeispiel:
Delphi-Quellcode:
function TInterfacedObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Destroy;
end;
Diese Vorgehensweise von Delphi hat das Arbeiten mit Interfaces sehr leicht gemacht, es hatte aber auch seine Tücken. Man sollte ein Objekt
besser nie sowohl über Objekt-Variablen als auch über Interface-Variablen ansprechen. Sonst konnte es zu verschiedenen Zugriffsverletzungen (
AV) kommen. Ein kurzes Beispiel:
Delphi-Quellcode:
var
Obj: TSomeInterfacedObject;
Intf: ISomeInterface;
[...]
Obj := TSomeInterfacedObject.Create;
Intf := Obj;
[...]
Obj.SomeMethod;
Intf.SomeInterfacedMethod;
Intf := nil;
[...]
Obj.SomeMethod;
[...]
In Zeile 5 erstellen wir ein Objekt vom Typ
TSomeInterfacedObject, welches das Interface
ISomeInterface implementiert. Jetzt wird in Zeile 6 der Variable
Intf eine Referenz auf das Interface
ISomeInterface, implementiert durch
Obj übergeben. Hier erhöht Delphi automatisch den Interface-Referenz-Counter, dieser wird
jetzt 1. Zur Zeit sind beide Instanzen (Obj und Intf) gültig. Somit ist der Zugriff in Zeile 8 auf
Obj.SomeMethod kein Problem, genauso kann in Zeile 9 auch auf
Intf.SomeInterfacedMethod; zugegriffen werden.
Nun wir in Zeile 10 der Interface-Verweis (Intf) auf
nil gesetzt, also gelöscht. Delphi implementiert jetzt automatisch den Aufruf zu
_Release und das Objekt dekrementiert seinen Referenz-Counter auf 0. Damit gibt es keine Interface-Variablen mehr, welche auf das Objekt verweisen und dieses gibt sich selbständig frei!
In Zeile 12 greifen wir mit
obj.SomeMethod nun wieder auf das bereits zerstörte Objekt zu. Je nach Aktion ist der weitere Verlauf der Applikation jetzt unvorhersehbar. Im besten Falle erkennt Windows das Problem und meldet eine Zugriffsverletzung und bricht die aktuelle Verarbeitung mit einer Fehlermeldung ab.