![]() |
AW: Schon wieder: Warum Interfaces II
Zitat:
Danke, stahli. Die Objekte nicht freigeben und dennoch kein Memoryleak erhalten - hm, da könnten doch nicht etwa Interfaces ins Spiel kommen...?! :thumb: Ich probiere das später mal aus. |
AW: Schon wieder: Warum Interfaces II
Stop! Nur weil man Interfaces benutzt bedeutet das nicht zwangsläufig, dass die implementierenden Instanzen automatisch freigegeben werden. Wenn man das möchte, leitet man zweckmäßigerweise von TInterfacedObject ab, falls nicht, von TInterfacedPersistent.
|
AW: Schon wieder: Warum Interfaces II
Und kann das richtig sein, dass die Objekte direkt von TObject abgeleitet sein sollen - das müsste ja in einem Cast von Object zu Interface enden... ?
|
AW: Schon wieder: Warum Interfaces II
Bei beiden explizit genannten Bibliotheken bin ich mit fast 100%ig sicher, dass hier Interfaces genutzt werden, um die automatische Speicherverwaltung zu nutzen. Wenn ich
Delphi-Quellcode:
oder gleichermaßen, was aus der OTL aufrufe, dann wird der Klump nach ausführung automatisch weggeräumt und ich muss das nicht selbst freigeben, als ob ich mir eine eigene TThread Klasse gebaut hätte oder so. Ja, TThread hat FreeOnTerminate, aber in den Bibliotheken passiert ja bissle mehr
AsyncCall(...)
|
AW: Schon wieder: Warum Interfaces II
Zitat:
Auf keinen Fall eine Basisklasse erstellen, die procedure Log einführt. (Warum, können wir später nochmal besprechen.) |
AW: Schon wieder: Warum Interfaces II
Weiteres Beispiel sind Binary übergreifende Objekte. So kann man die Funktionalität erweitern ohne eine wilde Sammlung von Methoden zu exportieren. Zum Beispiel bei Listen (ListViews oder Grids) habe ich gerne ein Popupmenu mit Funktionen. Wenn ich ein Untermenü zum Exportieren habe, kann ich dieses um Formate erweitern, in dem ich einfach eine weitere DLL ins Programmverzeichnis lege. Ein bisschen Pseudo Code:
Interfaces
Delphi-Quellcode:
DLL Auszug
IList = interface
['{1359E96A-BE76-4466-AFBA-CAFA950A4052}'] procedure First; procedure Prior; procedure Next; procedure Last; function FieldCount: Integer; function FieldCaption(AFieldNo: Integer): String; function FieldValue: Variant; function Eof: Boolean; end; IPlugin = interface ['{F2D42A98-ECC5-4633-A0E9-C90F1DAB7A88}'] end; IPluginManager = interface ['{FB80046D-24EB-4214-966D-D0F97699B3FC}'] procedure RegisterPlugin(APlugin: IPlugin); end; IListExporter = interface ['{B16E7F30-D6A7-4B83-870A-4E8B072949CC}'] procedure ExportList(AList: IList); function Caption: String; end;
Delphi-Quellcode:
Beim Programmstart wird die DLL geladen und die InitPlugin Prozedur aufgerufen.
TCsvExporter = class(TInterfacedObject, IPlugin, IListExporter)
public procedure ExportList(AList: IList); function Caption: string; end; procedure InitPlugin(AManager: IPluginManager); begin AManager.RegisterPlugin(TCsvExporter.Create); end; exports InitPlugin; Und so ungefähr wird das Menü aufgebaut
Delphi-Quellcode:
var
CurrentPlugin: IPlugin; tmpExporter: IListExporter; begin for CurrentPlugin in tmpManager.Plugins do begin if CurrentPlugin.GetInterface(IListExporter, tmpExporter) then pmExport.Add(tmpExport.Caption); end; Eine weitere Möglichkeit sind .Net Assemblys. Es gibt einige Frameworks für .Net die es für Delphi gar nicht oder nicht annährend in vergleichbarer Qualität gibt. Die benötigte Funktionalität kapsele ich dann in einer Klasse und diese wird per Interface bereitgestellt. Und noch ein (für uns/mich) sehr wichtiges Beispiel fällt mir ein: Delphi's Tools API (auch nur ein Plugin Beispiel). (In der echten Welt muss man natürlich noch auf Aufrufkonventionen und Datentypen/Marshalling achten....) |
AW: Schon wieder: Warum Interfaces II
Zitat:
|
AW: Schon wieder: Warum Interfaces II
Zitat:
|
AW: Schon wieder: Warum Interfaces II
Zitat:
Hier ist jetzt mal die "Hausaufgabe" von stahli (die Anzahl der geforderten Objekte von 5 hat mich zu einer freien Interpretation der Aufgabe angeregt):
Delphi-Quellcode:
Ich muss schon sagen, dass ich das dann doch nicht so locker hingeworfen habe, wie ich zuerst gedacht hatte. Knackpunkt war
unit Unit2;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,System.Generics.Collections,System.UITypes; type TForm2 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private procedure WMSysCommand(var Message: TMessage); message WM_SYSCOMMAND; public { Public-Deklarationen } end; IDrafi = Interface ['{876A9480-49D2-4735-A95F-AF21015CF372}'] function Kracks:integer; end; TMarmor = class(TInterfacedObject,IDrafi) function Kracks:integer; end; TStein = class(TInterfacedObject,IDrafi) function Kracks:integer; end; TUnd = class(TInterfacedObject,IDrafi) function Kracks:integer; end; TEisen = class(TInterfacedObject,IDrafi) function Kracks:integer; end; TBricht = class(TInterfacedObject,IDrafi) function Kracks:integer; end; procedure UnsereLiebe(LstDrafi:TList<IDrafi>); var Form2: TForm2; LstDrafi:TList<IDrafi>; implementation {$R *.dfm} procedure TForm2.Button1Click(Sender: TObject); var i,n:integer; Drafi:IDrafi; begin If Assigned(LstDrafi) then FreeAndNil(LstDrafi); LstDrafi := TList<IDrafi>.Create; LstDrafi.Capacity := 100; For i := 1 to 20 do begin For n := 1 to 5 do begin Case n of 1: Drafi := TMarmor.Create; 2: Drafi := TStein.Create; 3: Drafi := TUnd.Create; 4: Drafi := TEisen.Create; 5: Drafi := TBricht.Create; End; LstDrafi.Add(Drafi); end; end; UnsereLiebe(LstDrafi); end; procedure UnsereLiebe(LstDrafi:TList<IDrafi>); var i:integer; begin For i := 0 to LstDrafi.Count - 1 do If LstDrafi[i].Kracks = mrCancel then break; end; function TMarmor.Kracks:integer; begin Result := mrOK; Showmessage('Marmor...'); end; function TStein.Kracks:integer; begin Result := mrOK; Showmessage('...Stein...'); end; function TUnd.Kracks:integer; begin Result := mrOK; Showmessage('...und...'); end; function TEisen.Kracks:integer; begin Result := mrOK; Showmessage('...Eisen bricht.'); end; function TBricht.Kracks:integer; begin Result := MessageDlg('Abää unsärä Liihiebe nischt!',mtInformation,[mbOk,mbCancel], 0, mbOk); end; procedure TForm2.WMSysCommand(var Message: TMessage); begin If Message.WParam = SC_CLOSE then LstDrafi.Free; inherited; end; end.
Delphi-Quellcode:
. Die Tatsache, dass ein Interface ganz verschiedene Klassen aufnehmen kann, war mir natürlich klar, aber erst bei der konkreten Implementation (die TList kannte ja keine Typen) kam der Aha-Effekt.
Drafi := TMarmor.Create;
Jetzt kann ich mich vielleicht mal (wieder!) ![]() |
AW: Schon wieder: Warum Interfaces II
Super, so soll es sein. Durch praktische Verwendung habe ich es auch erst kapiert. Dann wieder nicht .... dann wieder ja ... dann doch irgendwie wieder nicht ..... und irgendwann war es verinnerlicht.
Wenn Du magst, können wir die Aufgabe noch etwas erweitern: Erstelle noch zwei Interfaces: IDoA mit Methode A und IDoB mit Methode B: Den ersten zwei Klassen weist Du ZUSÄTZLICH IDoA und dem ersten und den letzten 2 IDoB zu. Dann rufst Du für alle Objekte von den 100 (die IDoA unterstützen) A auf und analog noch B. Dazu musst Du Dir "Supports" anschauen. Das ist zwar auch ein Cast, aber etwas anders als bei Klassen und durchaus mit ein paar Vorteilen... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:55 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz