![]() |
Umgang mit Interfaces
Hallo!
Ich habe diesmal eine grundsätzliche Frage zum Umgang mit Interfaces, weil ich gerade eine entsprechendes Projekt erstelle. Gedacht habe ich mir das so: Es ibt eine Fülle von "kleinen" Interfaces die selber auch eine entsprechende Hierarchie haben (Details wie CC und GUID weggelassen):
Delphi-Quellcode:
Aus den Intefaces baue ich mir dann verschiedene Klassen wie zum Beispiel (ohne Code):
IBase=Interface(IUnknown)
function GetCanSave: Boolean; end; IValue=Interface(IBase) function GetIsNaN: Boolean; End; IInteger=Interface(IValue) function GetValue: Integer; procedure SetValue(const AValue: Integer); End; ICompare=Interface(IBase) function Compare: Boolean; End; ISomeThing=Interface(IBase) function DoSomeThing: Integer; End;
Delphi-Quellcode:
Alle diese Klassen werden nun in einer List verwaltet:
TMyClasses = Class(TInterfacedObject)
End; TMyBase = Class(TMyClasses, IBase) End; TMyInteger = Class(TMyBase, IInteger) End; TMyCompare = Class(TMyBase, ICompare) End; TMyDoSomething = Class(TMyCompare, IInteger, ISomeThing) fAValue: TMyClasses; End;
Delphi-Quellcode:
Das eigentliche Problem habe ich nun aber beim Arbeiten mit der TMyList. Sie ist mit meinen (polymorphen) Objekten befüllt und nun geht es darum möglichst elegant auf die einzelnen Methoden zugreifen zu können.
TListData =Class
fMy: TMyClasses; fName: String; fHash: Cardinal; End; TMyList = Class fList: TObjectList<TListData> End; Was funktioniert ist natürlich alle möglichen Klassen abzufragen und zu casten:
Delphi-Quellcode:
Das ist nicht besonders schön vor allem auch weil ich dann gerne SetValue unabhängig von der Klasse aufrufen möchte.
If fList[i].fMy Is TMyDoSomething Then
begin TMyDoSomething(fList[i].fMy]).SetValue(1); TMyDoSomething(fList[i].fMy]).DoSomething; If TMyDoSomething(fList[i].fMy]).fAValue Is TMyInteger Then begin TMyInteger(TMyDoSomething(fList[i].fMy]).fAValue).SetValue(2); end; end else if (fList[i].fMy Is TMyInteger) Then begin TMyInteger(fList[i].fMy).SetValue(1); end; Ein Zugriff über das Interface führt aber immer zu Schutzverletzungen (wegen der Referenzzählung) und wird in einem komplexeren System wohl schnell unüberschaubar:
Delphi-Quellcode:
Ich habe auch probiert die Referenzzählung "positiv" zu beeinflussen (_AddRef/_Release selber aufrufen) aber der Verwaltungsaufwand ist entsprechend groß und fehleranfällig wird das ganze auch.
If Supports(fList[i].fMy, IID_IInteger) then
begin (fList[i].fMy As IInteger).SetValue(1); end; Daher natürlich die Frage, wie kann man sowas besser organisieren? Ich dachte auch schon daran eine funktion in TMyClasses einzubauen die ein Interface liefert:
Delphi-Quellcode:
aber da schlägt die Referenzzählung auch zu (und kapselt eigentlich nur den Supports-Aufruf).
If fList[i].fMy.GetInterface(IID_xx, AIntegerIntf) Then
AIntegerIntf.SetValue(1); Jedes einzelne Interface in eine Klasse zu packen und dann mit implements in den TMyXXX-Klassen als Delegate einzuhängen ist ein großer Aufwand und nimmt die Flexibilität der Interfaces wieder heraus. Was wäre der beste Ansatz sowas möglichst transparent und sauber zu lösen? |
AW: Umgang mit Interfaces
Wenn du mit Interfaces arbeiten willst, dann verwende die Objekte auch durchgängig nur als Interfaces, dann wirst du auch keine Probleme mit der Referenzzählöung bekommen.
Delphi-Quellcode:
fMy: IMyBase;
Oder du verwendest Klassen "ohne" Referenzzählung und regelst die Speicherverwaltung nur über das Objekt. (.Free) Mir war so, als wenn Delphi schon eine Interface-Klasse ohne Referenzzählung besitzt (dachte die liegt neben TInterfacedObject), aber ich finde sie nicht. (TPersistent und TComponent gäbe es noch, aber die sind wohl schon zu aufgebläht) Notfalls einfach TInterfacedObject ableiten, bzw. in deiner Basisklasse das entsprechend überschreiben.
Delphi-Quellcode:
function TComponent._AddRef: Integer;
begin Result := -1; end; function TComponent._Release: Integer; begin Result := -1; end; Wozu existiert eigentlich TMyClasses, wo doch erst bei TMyBase/IMyBase die implemtierten Funktionen anfangen? Und die Klassen TMyInteger, sowie TMyCompare scheinen auch erstmal keine Funktionoalität zu beinhalten. |
AW: Umgang mit Interfaces
Zitat:
Delphi-Quellcode:
in System.Classes
TInterfacedPersistent
|
AW: Umgang mit Interfaces
Ich habe es jetzt nur überflogen, aber eine Standard-Implementation von durch Interfaces referenzierten Objekten ohne ARC ist
Delphi-Quellcode:
. Nicht nur der Name ist verwirrend, sondern auch der Namensraum. Es steckt in
TSingletonImplementation
Delphi-Quellcode:
. Merkwürdige Welt.
System.Generics.Defaults
|
AW: Umgang mit Interfaces
So nach dem Motto würde ich das machen, ungetestet:
Delphi-Quellcode:
TMyList = class
private var FList: TList<IListData>; public function Get<T: IInterface>(const AIndex: Integer): T; end; ... function TMyList.Get<T>(const AIndex: Integer): T; begin if not Supports(FList[AIndex], Result) then Result := nil; |
AW: Umgang mit Interfaces
Zitat:
System.Generics.Defaults gibt es bei mir nicht und wird es auch nicht geben. (Delphi wird langsam zu teuer) [edit] Mist, gibt es doch, aber nur daheim und hier im XE noch nicht. |
AW: Umgang mit Interfaces
Generics.Defaults gibt es auch schon in XE (vermutlich bereits ab 2009, aber das habe ich nicht installiert).
|
AW: Umgang mit Interfaces
Ohhh mann, jetzt fällt es mir wie schuppen von den Augen .... noch ein Namespace davor. :wall:
Nur voll bescheuert, daß die Codevervollständigung nur nach Wortanfängen sucht und man jetzt nix mehr findet. z.B. "SysUtils" > Strg+Enter > Namespace kopieren > Strg+F4 und reinkopieren ist doch keine Lösung, bis man sich den Mist endlich gemerkt hat. |
AW: Umgang mit Interfaces
Zitat:
Die Lösung von jaenicke mit einer generischen TList<T> ist das auf jeden Fall besser als eine TObjectList. Für deine Zwecke wäre sogar ein generisches TDictionary besser, da du ja irgendwie auf die Objekte per Namen zugreifen willst (siehe TListData.fName). Das ständige Rumgecaste mit unlesbaren Code (
Delphi-Quellcode:
)
TMyInteger(TMyDoSomething(fList[i].fMy]).fAValue).SetValue(2);
führt nur zu Problemen und in drei Monaten verstehst du es selber nicht mehr. Die Referenzzählung selber verursacht die Schutzverletzung nicht, sondern der fehlerhafte Umgang mit Interfaceobjekten. Kleiner Tipp: Es ist möglich in Interfaces mit propertys zu arbeiten. Find ich angenehmer beim Programmieren:
Delphi-Quellcode:
IInteger=Interface(IValue)
function GetValue: Integer; procedure SetValue(const AValue: Integer); property Value : interger read GetValue write SetValue; end; |
AW: Umgang mit Interfaces
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Hier schreibe ich bspw. in den uses nur "generic" und er bietet mir gleich die beiden passenden Units an. |
AW: Umgang mit Interfaces
Zitat:
Mutet zwar im Jahr 2013 etwas komsich an, aber irgendwie muss man seinen Kopf ja auch trainieren :spin: |
AW: Umgang mit Interfaces
Zitat:
Delphi-Quellcode:
unit Unit2;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Generics.Collections, Vcl.StdCtrls; type IBase = Interface(IUnknown) function GetCanSave : Boolean; end; IValue = Interface(IBase) function GetIsNaN : Boolean; End; IInteger = Interface(IValue) function GetValue : Integer; procedure SetValue(const AValue : Integer); End; ICompare = Interface(IBase) function Compare : Boolean; End; ISomeThing = Interface(IBase) function DoSomeThing : Integer; End; TMyBase = Class(TInterfacedObject, IBase) public function GetCanSave : Boolean; End; TMyInteger = Class(TMyBase, IInteger) public function GetValue : Integer; procedure SetValue(const AValue : Integer); function GetIsNaN : Boolean; End; TMyCompare = Class(TMyBase, ICompare) public function Compare : Boolean; End; TMyDoSomething = Class(TMyCompare, IInteger, ISomeThing) public function DoSomeThing : Integer; function GetValue : Integer; procedure SetValue(const AValue : Integer); function GetIsNaN : Boolean; End; TForm2 = class(TForm) Button1 : TButton; procedure FormCreate(Sender : TObject); procedure Button1Click(Sender : TObject); private { Private declarations } fList : TDictionary<string, IBase>; public { Public declarations } end; var Form2 : TForm2; implementation {$R *.dfm} { TMyCompare } function TMyCompare.Compare : Boolean; begin end; { TMyInteger } function TMyInteger.GetIsNaN : Boolean; begin // end; function TMyInteger.GetValue : Integer; begin // end; procedure TMyInteger.SetValue(const AValue : Integer); begin // end; { TMyDoSomething } function TMyDoSomething.DoSomeThing : Integer; begin // end; function TMyDoSomething.GetIsNaN : Boolean; begin // end; function TMyDoSomething.GetValue : Integer; begin // end; procedure TMyDoSomething.SetValue(const AValue : Integer); begin // end; { TMyBase } function TMyBase.GetCanSave : Boolean; begin // end; procedure TForm2.FormCreate(Sender : TObject); begin fList := TDictionary<string, IBase>.Create; fList.Add(TMyInteger.ClassName , TMyInteger.Create); fList.Add(TMyCompare.ClassName , TMyCompare.Create); fList.Add(TMyDoSomething.ClassName , TMyDoSomething.Create); end; procedure TForm2.Button1Click(Sender : TObject); var MyObject : IBase; begin if fList.TryGetValue(TMyDoSomething.ClassName, MyObject) then begin IInteger(MyObject).SetValue(1); ISomeThing(MyObject).DoSomeThing; end; if fList.TryGetValue(TMyInteger.ClassName, MyObject) then begin IInteger(MyObject).SetValue(2); end; if fList.TryGetValue(TMyCompare.ClassName, MyObject) then begin ICompare(MyObject).Compare; end; end; end. |
AW: Umgang mit Interfaces
Zitat:
Aber, sobald von einem "referenzzählenden" Objekt eine Interface-Referenz "erzeugt" wurde, dann übernimmt die Referenzzählung die Kontrolle über die Speicherverwaltung. Sobald dann die letzte Referenz weg ist, wird das Objekt freigegeben und dein Objektzeiger wird ungültig. Ohne Interfacereferenzen bleibt die Speicherfreigabe dem .Free überlassen. Darum war auch mein Tipp: Wenn mit "referenzzählenden" Interfaces gearbeitet werden soll, dann möglichst durchgehend nur noch mit Interface-Referenzen arbeiten und wenn Objekt-Referenzen nötig sind, dann muß man dort eben aufpassen. |
AW: Umgang mit Interfaces
Zitat:
|
AW: Umgang mit Interfaces
Ja ich weiß - Das Teil ist sein Geld wirklich mehr als wert.
Nach dem letzten Einkauf (TChart Pro) will ich vorerst nicht schon wieder laut nach weiteren Investitionen schreien ;-) Vorerst |
AW: Umgang mit Interfaces
Zitat:
Die Get-Funktion wird dann zu:
Delphi-Quellcode:
wobei das Resultat auf IInterface-Support eingeschränkt wird(?). Da man aber Supports nicht ohne eine IID_xxx aufzurufen kann, muss man hier wieder ein konkretes Interface angeben. Also etwa:
function TMyList.Get<T>(const AIndex: Integer): T;
begin if not Supports(FList[AIndex].fMy, Result) then //<- Error Result := nil; end;
Delphi-Quellcode:
Womit eigentlich nur mehr das IBase-Interface von der Funktion zu haben wäre? Ausprobieren konnte ich das ganze nicht, denn obwohl sich der Code der Liste compilieren lässt, bekomme ich dann bei einem Zugriffsversuch vom Compiler die Meldung: "E2531 Methode 'Get' erfordert explizite Typargumente", was eventuell daran liegt, dass ich keinen korrekten Aufruf hinbekommen habe..
function TMyList.Get<T>(const AIndex: Integer): T;
begin if not Supports(FList[AIndex].fMy, IID_BASE, Result) then Result := nil; end;
Code:
i: IBase;
i := fMyList.Get(0); i := fMyList.Get(0) As IBase; ... Wünschen würde ich mir ja eher sowas wie:
Delphi-Quellcode:
Unabhängig von der korrekten Implementierung frage ich mich auch noch wie die Performanz hier aussieht. Es werden ja ständig Interfaces über Strings gesucht?
Var
iSom: ISomeThing; iInt: IInteger; begin for i := 0 To fMyList.Count-1 Do begin if fMyList.Get(i, iInt) Then iInt.SetValue(Random(100)); if fMyList.Get(0, iSom) Then iSom.DoSomething; .. |
AW: Umgang mit Interfaces
Zitat:
![]()
Delphi-Quellcode:
Wobei IID hier auch direkt ein Interface-Typ sein kann (IMyFunnyInterface, IInteger, IDoSomeThing).
function Supports(const Instance: IInterface; const IID: TGUID; out Intf): Boolean;
function Supports(const Instance: TObject; const IID: TGUID; out Intf): Boolean; function Supports(const Instance: IInterface; const IID: TGUID): Boolean; function Supports(const Instance: TObject; const IID: TGUID): Boolean; function Supports(const AClass: TClass; const IID: TGUID): Boolean; Codebeispiel anhand deines Falls:
Delphi-Quellcode:
type
TMyDoSomething = Class(TMyCompare, IInteger, ISomeThing) ... end; var MySomeThingObject : ISomeThing; begin MySomeThingObject := TMyDoSomething.Create; if Supports(MySomeThingObject, IInteger) then begin ShowMessage('IInteger wird voll unterstützt!!!'); end; end; |
AW: Umgang mit Interfaces
Zitat:
Edit: Habe nicht erwähnt das ich meine Klassen nicht als Interface speichern kann (ISomeThing muss zu TSomeThing werden), weil ich eben eine "Basisliste" mit allen Klassen habe. Diese stehen untereinander in verschiedenen Beziehungen und pflegen daher eigene TNodeList-en um eine Referenz auf diese benötigten Instanzen zu haben. Durch diese Verlinkung schnellt der _RefCount bei verwendung von Interfaces aber hoch und am Programmende, wenn die "Basisliste"-alles frei gibt, bleibt ein riesiges Memoryleek über. |
AW: Umgang mit Interfaces
Zitat:
Dann solltest du die Referenzzählung auf jeden Fall in allen Objekten deaktivieren. Es macht keinerlei Sinn die zu nutzen, wenn du nicht durchgängig mit Interfaces arbeitest. Das haben wir als Notlösung bei der Umstellung in alten Quelltexten vorübergehend auch gemacht, sind aber mittlerweile so weit, dass wir es größtenteils richtig, sprich nur mit Interfaces, umgesetzt haben. Das ist bei Verwendung von Interfaces ja die einzige sinnvolle Variante. |
AW: Umgang mit Interfaces
Zitat:
Delphi-Quellcode:
aufrufst, bevor du es in deine Liste speicherst. Damit sagst du der Referenzzählung ja, dass da noch eine Referenz ist. Bevor du die Klasse dann freigibst musst du diese Referenz dann wieder mit
_AddRef
Delphi-Quellcode:
lösen und wenn keine andere Referenz auf eines der Interfaces der Klasse vorhanden ist, sollte sie auch gleich automatisch freigegeben werden. :?
_Release
Gruß, Sven |
AW: Umgang mit Interfaces
Ändere einfach in deinem Ausgangspost fMy von TMyClasses auf IInterface und benutz dann die Version mit Supports (GUIDs auf den Interfaces nicht vergessen) - dann zerbröselt es dir deine Referenzzählung nicht.
|
AW: Umgang mit Interfaces
Oder sogar doppelt ... falls die Basis-Instanz als Klasse benötigt wird
Delphi-Quellcode:
Und beim Destroy von TListData einfach mal nicht die Objekt-Instanz befreien :)
TListData = class
private fAsIntf : IInterface; fAsObj: TMyClasses; fName: String; fHash: Cardinal; public property AsIntf : IInterface read fAsIntf; property AsObj : TMyClasses; property Name : string read fName; property Hash : Cardinal read fHash; End; |
AW: Umgang mit Interfaces
Zitat:
Was unter der Überschrift "Warnung" als kryptischer 53 Wörter Satz steht, heißt nicht weiter als: "Pass ja auf wenn du Interface- mit Objektreferenzen mischt, dass kann nämlich ganz schön in die Hose gehen wenn du mit Supports arbeitest." Auch hier gilt: Nicht die Supports-Funktion ist schlecht oder fehlerhaft, sondern der Programmierer hat einen Fehler gemacht! Um beim Beispiel zu bleiben, das hier funktioniert so wie es soll:
Delphi-Quellcode:
Während das hier schief geht, wenn in DoSomeThing auf objekteigene Werte zugegriffen wird, denn die sind dann schon freigeben und genullt:
var
MySomeThingObject : ISomeThing; // <--- das ist so richtig begin MySomeThingObject := TMyDoSomething.Create; if Supports(MySomeThingObject, IInteger) then begin ShowMessage('IInteger wird voll unterstützt!!!'); end; MySomeThingObject.DoSomeThing; end;
Delphi-Quellcode:
var
MySomeThingObject : TMyDoSomething; // <--- da ist die entscheidene Stelle, Fehler!!! begin MySomeThingObject := TMyDoSomething.Create; if Supports(MySomeThingObject, IInteger) then begin ShowMessage('IInteger wird voll unterstützt!!!'); end; MySomeThingObject.DoSomeThing; end; |
AW: Umgang mit Interfaces
Zitat:
Eine solidere Lösung wäre einen Compiler zu verwenden, der besser ist (z.B. FreePascal mit "Corba-Interfaces"). Wenn man bei Delphi bleibt muss man eben den unsichtbaren Refenz-Zähler Kram so gut es geht aushebeln und höllisch aufpassen. P.S: Zur Laufzeit mit Supports zu arbeiten ist etwas, was man so gut es geht vermeiden sollte. Eine Haupterrungenschaft der Objektorientierung war, dass man statt mit schwammigen Pointern jetzt mit feste Typen arbeiten kann. Wenn man jetzt wieder anfängt zur Laufzeit Pointer herumcasten ist das irgendwie sehr Retro. |
AW: Umgang mit Interfaces
Zitat:
Zitat:
|
AW: Umgang mit Interfaces
Zitat:
Die "automatische" Referenzverwaltung von Inferface-Objekten ist in meinen Augen sehr bequem und ermöglicht ein sehr komfortables modernes programmieren. Ähnliches wurde ja im Next-Gen-Compiler für mobile Platformen von Emba umgesetzt. Zitat:
Oft liegt der Fall vor, dass ein Objekt zwei bis drei Interfaces implementiert. Je nachdem muss man dann zwangsläufig testen, ob ein bestimmtes Interface unterstützt wird. Siehe auch die obigen Beispiele. Hast du nur ein Objekt von Typ IInteger vorliegen, musst aber schauen ob ISomeThing unterstützt wird (oder umgekehrt) -> wie sollte man es sonst machen? |
AW: Umgang mit Interfaces
Eine Frage zum Verständnis für jemanden, der mit Interfaces noch nie zu tun hatte:
Delphi-Quellcode:
Sehe ich das richtig, das mein
var
MySomeThingObject : ISomeThing; // <--- das ist so richtig begin MySomeThingObject := TMyDoSomething.Create; if Supports(MySomeThingObject, IInteger) then begin ShowMessage('IInteger wird voll unterstützt!!!'); end; MySomeThingObject.DoSomeThing; end;
Delphi-Quellcode:
bei Verlassen der Prozedur automatisch freigegeben wird, obwohl ich zuvor mit
MySomeThingObject
Delphi-Quellcode:
Speicher dafür alloziert habe ?
TMyDoSomething.Create;
|
AW: Umgang mit Interfaces
Wenn in TMyDoSomething Referenzzählung enthalten ist (z.B. weil von TInterfacedObjekt abgelitten), dann ja, wird es bei Erreichen von 0 Referenzen automatisch freigegeben.
|
AW: Umgang mit Interfaces
Und da die einzige Referenz darauf eine lokale Variable war, ist der Referenzzähler beim Verlassen der Methode Null.
Nicht nur beim normalen Verlassen, natürlich auch, wenn deine Methode mittendrin per Exception rausfliegt. Damit spart man sich das ganze nervige
Delphi-Quellcode:
-Geraffel.
try..finally..Destroy
|
AW: Umgang mit Interfaces
Zitat:
|
AW: Umgang mit Interfaces
Ich habe in meinem Leben bislang nie den Mut gehabt, mir den Assemblercode anzuschauen, der für die Referenzzählung oder das Werfen einer Exception entsteht.
Dann würde ich jedes mal wenn ich in einer Methode mittels Interface-Variable über irgendetwas iterieren,ein schlechtes Gewissen bekommen und würde meines Lebens nicht mehr froh. Ja, mir ging es jetzt nur um meinen persönlichen Komfort im Quelltext. Wie sehr der PC dann später zusätzlich schuften muss habe ich jetzt nicht bedacht... |
AW: Umgang mit Interfaces
Zitat:
Mit einem normalen Objekt hättest du ein Speicherleck! |
AW: Umgang mit Interfaces
Zitat:
|
AW: Umgang mit Interfaces
Zitat:
Delphi ruft z.B weiterhin lustig die Refenz-Count Methoden von Objekten auf, auch wenn das Objekt z.B. schon freigegeben wurde. Die zur Zeit einzig sinnvolle Lösung für das Problem nennt sich Free-Pascal... Zitat:
Das ist fast genauso beknackt wie die Forderung der ActiveX Retro-Gurus, dass man Objekte und Interfaces nicht mischen sollte. Leute. Nehmt einfach einen vernünftigen Compiler und reitet nicht für immer rum auf diesen Lehrsätzen für COM-Interfaces, die Microsoft mal in der Antike verkündet hat. Zitat:
|
AW: Umgang mit Interfaces
Zitat:
|
AW: Umgang mit Interfaces
Zitat:
Kannst du hierauf nochmal eingehen? Das es COM-Interfaces sind - also einen gewissen sprachübergreifenden Standard folgen - ist jetzt aber nicht das "Problem", oder? Zitat:
Solange du in den selbstimplementierten _AddRef und _Release Methoden kein Schindluder betreibst geht das doch?!? Wo ist das Problem? Oder verhält sich das in Free-Pascal anderes?
Delphi-Quellcode:
ISomeThing = Interface(IInterface)
['{16CCA417-F4C4-4B4A-88CE-FDD79B875876}'] function DoSomeThing : Integer; End; TMyInterfacedObject = class(TObject, IInterface) protected function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end; TMyDoSomething = Class(TMyInterfacedObject, ISomeThing) protected function DoSomeThing : Integer; end; ... function TMyInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult; begin if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE; end; function TMyInterfacedObject._AddRef: Integer; begin Result := -1; end; function TMyInterfacedObject._Release: Integer; begin Result := -1; end; ... var MySomeThingObject : ISomeThing; begin MySomeThingObject := TMyDoSomething.Create; MySomeThingObject.DoSomeThing; (MySomeThingObject as TMyDoSomething).Free; // folgendes funktioniert problemlos, obwohl das Objekt freigeben wurde! MySomeThingObject._AddRef; MySomeThingObject._AddRef; MySomeThingObject._AddRef; MySomeThingObject._Release; end; Zitat:
Es gab immer Leute die meinen es besser zu wissen. :stupid: COM ist aktueller denn je, weiß nicht was das mit Antike zu tun haben sollte. Mein Rechner läuft auf Windows 8.1 und da ist das "Kachelstartmenü" durchgehend mit dieser Technologie umgesetzt. Zeige uns doch bitte mal einen typischen Anwendungsfall in deiner Applikation, wo der Einsatz von COM-Interfaces zu Fehlern führt und stattdessen das von dir gewünschte Verhalten besser wäre. In meiner Applikation sind ein Großteil der Klassen über Interfaces ansprechbar und das nicht mischen von Objekt- und Interfacereferenzen gewöhnt man sich auch schnell an. Weiß nicht worin da die Schwierigkeit besteht das konsequent durchzuziehen. Gleichermaßen könnte man sich darüber aufregen, dass der Compiler nicht folgendes zur Compilezeit anmeckert:
Delphi-Quellcode:
var a, b, c : Integer; begin a := 0; c := b div a; end; |
AW: Umgang mit Interfaces
Zitat:
Wenn du "referenzählende" Variablen hast, und du denen unterm Arsch weg die Objekte klaust, wovon die natürlich nichts mitbekommen, dann können die nur davon ausgehen, daß die darin verlinkte Instanz gültig ist und es werden die Referenzen gezählt (bzw. es wird versucht). - Entweder es wird über die Referenzzählung der Speicher freigegeben - und wenn nicht, dann darf die Variable nicht referenzzählend sein - oder du mußt die referenzzählende Variable auf nil setzen (unter böswilliger Umgehung der Referenzzählung), sobald du das Objekt freigibst, oder bevor die Variable freigegeben wird, bzw. bevor sie einen neuen Wert bekommt - und es darf auch keiner mehr die "ungültige" Referenz daraus verwenden Zitat:
B ist nicht initialisiert :stupid: Zitat:
Das kann man aber zum Debuggen beheben. - FastMM entsprechend einstellen - oder einen Debug-Speichermanager verwenden welche die Objektinstanzen zerstören/überschreiben, womit des beim nächsten Zugriff knallt PS: Zugrif auf "ungültige" Zeiger .... wie oben erwähnt. Das Objekt ist weg, aber DU hast den Instanzzeiger in MySomeThingObject nicht bereinigt. |
AW: Umgang mit Interfaces
Zitat:
Zitat:
Solange der Pointer nicht ungültig wird, wird ja auch beliebig in die Methoden gesprungen. Wenn hier fröhlich Interface- und Objektreferenzen gemischt werden (sollt ihr dafür alle in der Hölle schmoren) dann geht auch folgends:
Delphi-Quellcode:
Von daher soll der Compiler doch ruhig in die eigenen _AddRef- und _Release-Methoden springen, wenn man meint sowas brauchen zu müssen!
function TMyDoSomething.DoSomeThing : Integer;
begin Result := 123; end; procedure TForm1.Button2Click(Sender: TObject); var MySomeThingObject : TMyDoSomething; MagicNumber : Integer; begin // MySomeThingObject := TMyDoSomething.Create; MagicNumber := MySomeThingObject.DoSomeThing; // MagicNumber hat jetzt den Wert 123, obwohl kein Objekt erzeugt wurde! Schwarze Magie? ;-) end; |
AW: Umgang mit Interfaces
Liste der Anhänge anzeigen (Anzahl: 1)
Ich seh schon ... heißes Thema :twisted: ...
Da es mir aber um die Implementierung mit Interfaces geht habe ich das ganze nochmals vereinfacht und komplett auf Interfaces umgestellt. Dazu gibts ein eigenes IAdmin-Interface in dem die Sachen definiert sind die vorher eben nur in der Objektinstanz zur Verfügung standen. Die Implementierung habe ich hier angehängt weil es noch ein Problem bei der Verwaltung der Interfaces gibt und ja, ohne FastMM gibts keine vernünftige Delphi-Entwicklung :stupid:! Nochmals zur Erklärung: Die "Hauptliste" (fIntfLst) wird aus einer Textdatei erstellt, es gibt um die 20 verschiedenen Klassen die aus der Kombination von ca. 10 Interfaces bestehen. Diese "Knoten" stehen noch untereinander in Beziehung und daher speichern die Knoten diese Beziehungen in privaten Listen (wie z.B.: TTest.fMyNodes)... Eigentlich dachte ich ja, das bei reiner Interface-Verwendung keine Probleme mehr auftreten, aber irgendwie schaffe ich es durch meine Querverlinkung die Referenzzählung durcheinander zu bringen (siehe uImpl.pas Zeile 170)... |
AW: Umgang mit Interfaces
Wenn der Speichermanager aufräumt, dann wird es auch knallen. Oder wenn inzwischen etwas Anderes an der Stelle steht, dann produziert man damit einen Buffer-Overrun,
wenn man auf ungültige Variablen zugreift. Und das ist nicht erst seit Interfaces so.
Delphi-Quellcode:
var
o: TMyIrgendwas; o := TMyIrgendwas.Create; o.Free; o.DoWas; // Fehler o := TMyIrgendwas.Create; FreeAndNil(o); o.DoWas; // Zugriffsverletzung bei Addresse 0 Man kann gern die Speicherverwaltung komplett der Referenzzählung des Interfaces überlassen. Und wenn es unbedingt sein muß, dann holt man sich "kurzzeitig" nochmal eine Objekt-Referenz aus dem Interface raus (an dieser Stelle muß parallel eine Interface-Referenz vorhanden sein), denn Delphi bietet seit einer Weile ein Pseudo-Interface an, welches die Objektreferenz zurückgibt. (wenn im Interface ein Delphi-Objekt steckt) Und an diese Objektreferenz kommt man z.B. via
Delphi-Quellcode:
ran.
intf AS TObject
Man kann natürlich auch eigene Getter in seinem Interface verbauen, welches Objekt-Instanzen zurückgibt. Sobald dann die "paralelle" Interface-Referenz freigegeben wird, ist die Objektreferenz als "ungültig" anzusehen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:15 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 by Thomas Breitkreuz