Delphis Schnittstellenfalle
Jetzt kenne ich zwei große Fallstricke:
Falle 1. Schnittstellentypkonvertierungsfalle
A) Sie können keine Objektreferenz auf eine Schnittstelle werfen, die den Typ dieser Referenz nicht deklariert, selbst wenn das Objekt diese Schnittstelle tatsächlich implementiert (hehe, sind die Vorteile schwer auszusprechen). b) Wenn eine Objektvariable einer Schnittstellenvariablen zugewiesen wird und die Objektvariable der Schnittstellenvariable zugewiesen wird, hat sich die Adresse der Objektvariablen geändert, dh nicht mehr das ursprüngliche Objekt, sondern zeigt auf eine Fehleradresse . Zum Beispiel: i1 = Schnittstelle
Funktion do: boolean;
Ende;
TC1 = Klasse
ATT1: Ganzzahl;
Ende;
TC2 = Klasse (TC1, I1)
ATT2: Ganzzahl;
Funktion do: boolean;
Ende; intf1: i1; obj1: tc!; obj2: tc2; obj2: = tc2.create;
OBJ1: = OBJ2.
I1 (obj2) .do;
I1 (OBJ1) .do; Da der Typ TC1 von OBJ1 nicht erklärt, dass I1 implementiert ist, kann er nicht in I1 konvertiert werden, selbst wenn OBJ1 I1 umsetzt. Wenn Sie das Objekt in eine Schnittstelle und dann zurück konvertieren, wird es Probleme geben. Obj2: = tc2.create; obj2.att1: = 0;
Intf1: = obj2; // korrekt. OBJ2: = INTF1; OBJ2.ATT1: = 0; Das heißt, nach der Konvertierung von Objektreferenz in Zeigerreferenz ändert sich die Adresse, aber wenn die Zeigerreferenz aus der Objektreferenz zurückgezogen und dann an Objektreferenz zurückgegeben wird (Delphis Fehler?).
Falle 2. Schnittstellenlebensdauermanagement
Basierend auf meinem gesunden Menschenverstand (hier ist das Programmieren des gesunden Menschenverstandes, nicht auf den gesunden Menschenverstand von Delphi), denke ich, dass Schnittstellen kein lebenslanges Management erfordern, da Schnittstellen überhaupt keine echten Objekte generieren können. Aber Delphi hat erneut meinen gesunden Menschenverstand getroffen (huh, warum sagst du "erneuern"?), Seine Schnittstelle hat eine Lebensdauer und muss die folgenden drei Methoden implementieren: Funktion queryInterface (const iid: tguid; out obj): hResult; stdcall; Ich weiß also nicht, wie ich diese drei Methoden implementieren soll. J Wenn Sie diese drei Methoden nicht selbst implementieren möchten, können Sie TComponent verwenden. Da TComponent diese drei Methoden implementiert hat, kann es daraus geerbt werden, sodass diese drei Methoden nicht implementiert werden müssen. Können Sie es mit Zuversicht verwenden? Die Antwort ist nein. Weil Delphi heimlich (weil es über meine Erwartungen hinausging), wenn Sie die Schnittstellenvariable auf NIL festlegen. Funktion _Intfclear (var dest: iInterface): Zeiger; var p: Zeiger; ). Funktion tcomponent._Release: Integer; Dann wird nichts nicht getan werden. Was wir nicht als COM -Objekte arbeiten, gibt es also kein Problem? Die Antwort ist immer noch nein, die folgende Situation: OBJ2: = TC2.create; tryintf1: = obj2; intf1.do; Es tritt ein illegaler Zugangsfehler der Adresse auf. Warum? Wie oben erwähnt, wird die Schnittstellenreferenz auf NIL gesetzt, und _Intfclear wird aufgerufen, und _IntfClear wird zu diesem Zeitpunkt die _release des Objekts aufgerufen. Einige Leute sagen, es ist unnötig? Obj2: = tc2.create; tryintf1: = obj2; intf1.do; Warum? Da der Delphi -Compiler klug ist, ist es der Meinung, dass Sie diese Adresse auf NIL festlegen, sodass Sie sie automatisch hinzufügen. Wie löst ich es? Methode 1: Setzen Sie zuerst die Schnittstellenreferenz auf NIL und geben Sie dann das Objekt frei. INTF1: = nil; Zeiger (INTF1): = NIL; Ich neige dazu, die zweite Methode zu verwenden, damit Sie nicht darüber nachdenken müssen, wer zuerst veröffentlichen soll. Darüber hinaus können Sie in einigen Entwurfsmustern nur Schnittstellenreferenzen halten, und Sie wissen nicht, wann das referenzierte Objekt freigegeben wird. Betrachten Sie beispielsweise das Verbundmuster. Tcomposite = class (tcomponent, i1) private interist: txcontainer; // eine containerklasse, die die Schnittstellenreferenz für "Blatt" speichert. Öffentliches Verfahren add (Aintf: i1); Offensichtlich nicht, so werden die "Blätter" definitiv später als dieses "Synthesisobjekt" -Objekt veröffentlicht? Ich glaube nicht. Wenn diese Verordnung vorgeschrieben ist, geht viel Flexibilität verloren. Wir denken also definitiv, dass es keine Beziehung zum ursprünglichen Objekt gibt, wenn diese Schnittstellenreferenzen platziert werden, um illegale Zugangs -Zugriffsfehler nach dem Objekt zu vermeiden. Welchen Container sollten Sie in Betracht ziehen? Array? Tlist? Tinterfacelist? Zunächst denke ich, dass es ein Tinterfacelist sein muss, denn das, was wir aufnehmen wollen, ist die Schnittstelle. Aber wenn es frei ist, ist es für Null alle Schnittstellen, die es aufnimmt, genau das, was wir nicht wollen. Oder wir können die Schnittstellenreferenz in einen Zeiger vor kostenlos konvertieren und dann auf NIL einstellen. für i: = 0 bis interstip.count -1 dopointer (interstip.Items [i]): = nil; Dann versuchen wir das Array. Interlist: Array von i1; dynamische Arrays sollten nicht freigegeben werden. Es scheint sehr nützlich zu sein, aber wenn der Compiler es freigibt, wird es immer noch jedes Element auf Null setzt, und als Schnittstelle besteht immer noch die Möglichkeit illegaler Adresszugriffsfehler. Es kann auf diese Weise für i: = niedrig (arr) bis hoher (arr) Dopointer (arr [i]) durchgeführt werden: = nil; Zeit, die Sie verwenden, und Sie können es nicht erinnern, es zu tun oder nicht. Verwenden Sie schließlich die TLIST. Es gibt jedoch einen Zeiger in der Tlist. Wenn Sie also hinzufügen, müssen Sie wie folgt fortfahren: xxx.add (Aintf: i1) mit der Interlust.Add (Zeiger (Aintf)); Ende; Bedarf für eine besondere Verarbeitung beim Verlassenen. Es scheint perfekt zu sein, aber es gibt immer noch eine Falle. interlist.add (tc2.create); Da es sich um einen reinen Zeiger handelt, wird bei der Konvertierung in eine Schnittstelle die Adressumwandlung des auf die Schnittstellenreferenz verwiesenen Objekts nicht durchgeführt (es weiß nicht, wie es geht). Wenn Sie also die von der Schnittstelle deklarierte Methode aufrufen, wird er aufgerufen. Es ist ein weiterer illegaler Adresszugriffsfehler. Dies ist der einzige Weg zum Schreiben: interLit.add (Zeiger (i1 (tc2.create)); obwohl es ein bisschen problematisch ist, ist dies die beste Lösung (wie ich weiß). Wenn Sie vergessen, dass die Übertragung beim ersten Anruf einen Fehler macht, ist es einfacher, Fehler zu finden (im Vergleich zur Verwendung von Array). Daher schlage ich vor: 1. Verwenden Sie die Tlist, um Schnittstellenreferenzen zu verwalten. 2. Wenn Sie das Objekt hinzufügen, sollten Sie das Objekt in eine Schnittstelle und dann in einen Zeiger umwandeln. 3.. Für Schnittstellenreferenzen, die nicht mit der TLIST verwaltet werden. Als Verweis auf Schnittstellen müssen Sie nach der folgenden Methode manuell auf NIL einstellen: Zeiger (intfref): = nil; Wenn Sie sich daraus erbt, können Sie sich daher auch die Mühe ersparen, diese drei Methoden zu implementieren. Seine _Release ist jedoch wie folgt implementiert: Funktion tinterFaceObject._Release: Integer; Das Objekt. So wird es implementiert, es bringt mehr Schwierigkeiten als die Erben von TComponent. Sofern nicht anders verwendet, wird nicht empfohlen. Alle Diskussionen über Schnittstellen oben sind auf die im Allgemeinen verwendeten Schnittstellen auf Sprachebene beschränkt, und es gibt keine Diskussion über COM+ -Schnizeln. Die Implementierung dieser seltsamen Schnittstellen von Delphi hat viel damit zu tun, dass sie sich von der COM+ Grenzfläche entwickelt, was das Ergebnis des Kompromisses aufgrund der schweren historischen Belastung ist.