![]() |
Zirkuläre Unit Reference
Hi,
ich schreibe gerade einen Quellcodegenerator der aus einer Datenbank eine Mittelschicht erzeugt (oder besser die Grundlage dafür). Für jede Klasse wird eine eigene Unit erzeugt. Beispiel für mein Problem. Tabellen Adresse------<Matchcode Eine Adresse hat N Matchcodes. Ein Matchcode aber immer nur eine Adresse. Lösung für die Zwischenschicht Adresse.Liste_Matchcodes.Matchcode (in Adresse wird in der uses Liste_Matchcodes verwendet, in der uses von List_Matchodes Matchcode) Matchcode.Adresse (in Matchcode wird in der uses Adresse Verwendet) Probleme Ich will die oben beschrieben Funktionalität, aber nicht alle Klassen in eine Unit schreiben. Die Uses müssen im Interface-Bereich stehen da ich die Objekte als Klassenvariablen verwende. Lösungsansätze ![]() Zitat:
![]() Zitat:
|
Re: Zirkuläre Unit Reference
Zitat:
Delphi-Quellcode:
und später richtig deklarieren.
type TKlassenNamen = class;
|
Re: Zirkuläre Unit Reference
So einfach das klingt ich mache es wohl falsch.
Beispiele
Delphi-Quellcode:
Fehlermeldung: Typ list_ADD_MATCHCODE ist nicht vollständig definiert.
unit ADDRESS;
interface uses Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser, datastructs; type list_ADD_MATCHCODE = class; type //*********************** //****Kopf Basisklasse*** //*********************** TADDRESS = class(TBase) .... implementation uses list_ADD_MATCHCODE;
Delphi-Quellcode:
Fehlermeldungen:
unit ADDRESS;
interface uses Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser, datastructs; type type list_ADD_MATCHCODE = class; //*********************** //****Kopf Basisklasse*** //*********************** TADDRESS = class(TBase) .... implementation uses list_ADD_MATCHCODE; Bezeichner erwartet aber Type gefunden. Typ list_ADD_MATCHCODE ist nicht vollständig definiert. [edit=SirThornberry]Zitat-Tags durch Delphi-Tags ersetzt - Mfg, SirThornberry[/edit] |
Re: Zirkuläre Unit Reference
Heißt deine Unit genauso wie der Typ? :gruebel:
Verwendet TAddress die Klasse list_ADD_MATCHCODE oder warum muss list_ADD_MATCHCODE vorher definiert werden? Ich glaub mit einer dritten Unit wäre das lösbar. |
Re: Zirkuläre Unit Reference
Delphi-Quellcode:
Klappt auch nicht sieht aber richtiger aus.
unit ADDRESS;
interface uses Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser, datastructs; type Tlist_ADD_MATCHCODE = class; //*********************** //****Kopf Basisklasse*** //*********************** TADDRESS = class(TBase) .... implementation uses list_ADD_MATCHCODE; Unitname: list_ADD_MATCHCODE Typname: Tlist_ADD_MATCHCODE |
Re: Zirkuläre Unit Reference
Ja, klappen wird das so nicht. Die Declaration muss vollständig im Interfaceteil abgeschlossen werden.
|
Re: Zirkuläre Unit Reference
Dann wäre es ja so richtig:
Delphi-Quellcode:
Bringt aber die Fehlermeldung: Typ list_ADD_MATCHCODE ist nicht vollständig definiert.
unit ADDRESS;
interface uses Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser, datastructs; type Tlist_ADD_MATCHCODE = class; type //*********************** //****Kopf Basisklasse*** //*********************** TADDRESS = class(TBase) .... implementation uses list_ADD_MATCHCODE; |
Re: Zirkuläre Unit Reference
Hallo,
der Ansatz in Beitrag #3 war schon richtig. Du darfst aber nicht Folgendes machen:
Delphi-Quellcode:
sondern Folgendes:
type
TClass1 = class; type TClass2 = class(TObject) FClass1: TClass1; end; TClass1 = class(TObject) FClass2: TClass2; end;
Delphi-Quellcode:
Beachte: Es gibt nur eine Deklaration (ein "type").
type
TClass1 = class; TClass2 = class(TObject) FClass1: TClass1; end; TClass1 = class(TObject) FClass2: TClass2; end; Gruß xaromz |
Re: Zirkuläre Unit Reference
Das entspricht meinem Beitrag 5, funktioniert aber leider auch nicht.
Aber ich weiß jetzt warum, ich hatte bei der deklaration vergessen das alle Klassen von TBase abgeleitet sind. Habe es gerade geändert und es funktioniert.
Delphi-Quellcode:
Vielen Dank euch allen
unit ADDRESS;
interface uses Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser, datastructs; type Tlist_ADD_MATCHCODE = class(TBASE); //*********************** //****Kopf Basisklasse*** //*********************** TADDRESS = class(TBase) .... implementation uses list_ADD_MATCHCODE; |
Re: Zirkuläre Unit Reference
Zitat:
|
Re: Zirkuläre Unit Reference
Nachdem ich zunächst dachte alle wird gut, musste ich feststellen das nur die geerbten Methoden und Eigenschaften übernommen werden :( (bei den Anderen Fehlermeldung: "Undefinierter Bezeichner")
Also habe ich zum test nicht mehr geerbt, nun kriege ich aber die Fehlermeldung: "Typ ... nicht vollständig definiert" Die Fehlermeldung lässt sich so umgehen:
Delphi-Quellcode:
Aber so sieht Delphi die Klasse leider als leere Klasse und ich kriege die Fehlermeldung: "Undefinierter Bezeichner"
type
TClass1 = class end; TClass2 = class FClass1: TClass1; end; TClass1 = class FClass2: TClass2; end; |
Re: Zirkuläre Unit Reference
ohne das end bei der Forward-Deklaration. Sonst denkt Delphi, die Klasse ist komplett, also so:
Delphi-Quellcode:
Type
TFoo=Class; TBar=Class FFoo: TFoo; End; TFoo=Class FBar: TBar; End; |
Re: Zirkuläre Unit Reference
Liste der Anhänge anzeigen (Anzahl: 1)
Das habe ich ja schon probiert. Passend zu dem Quelltext käme jetzt: Typ TFoo nicht vollständig definiert.
Ich habe ein kleines Beispiel gemacht. |
Re: Zirkuläre Unit Reference
Hallo,
ich habe Dir schon in meinem letzten Beitrag geschrieben, wie es geht. In Deinem Beispielcode deklarierst Du die beiden Klassen in verschiedenen Units, bzw. schreibst in der einen Unit eine Forward-Deklaration auf eine Klasse in einer anderen Unit. Das funktioniert natürlich nicht. Wenn Du Forward-Deklaration nutzt ("type TFoo = class;"), dann musst Du diese Klasse auch komplett in dieser Unit deklarieren. Gruß xaromz |
Re: Zirkuläre Unit Reference
Präziser:
Forward Deklarationen eines Types müssen im gleichen "Type" Block erfolgen samt ihrer vollständigen Deklaration. Ein regülärer Bezeichner auf Unitebene beendet eines Type Block automatisch. Ein regulärer Unitbezeichner sind "Unit", "uses", "implementation" und "interface". Ergo: mit Delphi gehen keine Forward Deklarationen über Unitgrenzen hinweg, noch in unterschiedlichen Type-Blöcken. Erweitere mal virtuell in Gedanken alle Sachen wie "const", "type", "implementation", "interface" nit einem sofortigem "begin end;" Block. Denn defakto existieren diese quasi im Compiler auch wenn wir sie nicht im Source schreiben müssen/dürfen. Dh. nach einem "type" stände sofort "begin end;" und dazwischen kannst du Forward Deklaration machen wie du möchtest Hauptsache du deklarierst diese Typen dann in diesem Block auch vollständig. Eine Deklaration innerhalb dieses "virtuellen" Bereiches ist nicht in anderen "Bereichen" sichtbar. Einzigste Ausnahme speziell auf den Bezeichner "type" bezogen ist die Deklaration eines neuen eigenständigen Standarddatentyps abgeleitet von einem anderen Datentyp, also sowas:
Delphi-Quellcode:
Gruß Hagentype //begin TTime = type TDateTime; TDate = type TDateTime; MyInteger = type Integer; //end; type //begin //end; |
Re: Zirkuläre Unit Reference
Liste der Anhänge anzeigen (Anzahl: 1)
Das Problem ließ sich mit noch ein bisschen externer hilfe lösen.
Es müssen zu den einzelnen Klassen Interfaces erstellt, und in einzelne Units gepackt werden. Ich habe für ein Beispiel geschrieben und für alle Interessierten in den Anhang gepackt. @xamroz Mein Problem war das es mir sehr wichtig ist die Unitunterteilung aufrecht zu erhalten. Ich dachte es wäre so möglich... |
Re: Zirkuläre Unit Reference
Hallo,
Zitat:
Zu Deinem Demoprogramm: - Du castest ein Interface auf das zugrundeliegende Objekt. Das ist nicht zulässig. - Du benutzt Objekte, ohne Instanzen zu erzeugen. Das fliegt Dir bald um die Ohren. Gruß xaromz |
Re: Zirkuläre Unit Reference
Neben den korrekten Bemerkungen von Xaromz noch als Bemerkung zusätzlich:
Das was du in deinem Testprojekt machts geht auch einfacher
Delphi-Quellcode:
Gruß hagenunit class1; interface type TClass1 = class private FClass2: TObject; public procedure BlaBla; end; implementation uses class2; procedure TClass1.BlaBla; begin (FClass2 as TClass2).BlaBla; end; unit class2; interface type TClass2 = class private FClass1: TObject; public procedure BlaBla; end; implementation uses Class1; procedure TClass2.BlaBla; begin (FClass1 as TClass1).BlaBla; end; |
Re: Zirkuläre Unit Reference
Dieser Weg soll der sauberere sein, die andere Möglichkeit wäre alles von einem Basisobjektabzuleiten, dann statt dem Interface über das Basisobjekt die Typen zu deklarieren, und am ende wie bei dem Interface zu casten.
@xaromz [qoute]Ich hoffe, Du weißt, was Du da tust. Interfaces sind eine sehr fehlerträchtige Sache. Insbesondere durch die gegenseitige Abhängigkeit der beiden Interfaces wirst Du vermutlich ein Speicherleck bekommen. Für diesen Zweck sind Interfaces [/quote] Ich werde wie gesagt den Quellcode erzeugen, also muss ich es nur einmal richtig hin bekommen, und alle interfaces sind richtig. Zitat:
In meinem echten Projekt werden selbst verständlich alle Objekte instanziiert. @NegaH Du leitest statt von dem Interface von TObject ab, habe heute keine Zeit mehr das zu testen, werde ich aber auch ausprobieren. Hat es Nachteile? |
Re: Zirkuläre Unit Reference
Nein, hat keine Nachteile (mein Vorschlag oben), mal davon abgesehen das deine ganze Objektorientierte Konstruktion insich falsch sein muß.
Wenn du über solche Sachen wie oben mein Typcast oder den Interfaces herangehen musst dann stimmt irgendwas nicht an deinem Grundkonzept. Xaromz hat aber insofern Recht, das 1.) der Weg über die Interfaces ein unnötiger Überbau darstellt da wie in meinem Beispiel das Ganze auch ohne Interfaces geht 2.) du die Verwendung von Interfaces und Klassen nicht vermischen solltest da ansonsten das automatische Referencecounting und somit die Speicherfreigabe durcheinander kommt 3.) der Typcast eines Interfaces direkt in das implementierende Objekt in deinem Source vollkommen falsch ist 4.) der Typcast eines Interfaces in sein implementierendes Objekt grundsätzlich gegen die Regeln, bzw. dem Sinn der Interfaces ist Wenn du schon eine Referenz von einem Interface auf sein implementierendes Objekt erhalten möchtest dann so:
Delphi-Quellcode:
Aber!! zum Widersinnigen der Sache:type IMyInterface = interface function Implementor: TInterfacedObject; end; TMyImplementor = class(TInterfacedObject, IMyInterface) function Implementor: TInterfacedObject; end; function TMyImplementor.Implementor: TInterfacedObject; begin Result := Self; end; // oder gleich so, da Result -> EAX und Self -> EAX function TMyImplementor.Implementor: TInterfacedObject; begin end; Interfaces sollen die Implementierung einer Funktionalität im Zusammenhang mit dessen Schnittstellen Deklaration vollständig voneinander trennen. Es soll also keinerlei Zusammenhang zwischen einer deklarierten Schnittstelle zu der eigentlichen Implementierung dieser Schnittstelle bestehen, vollkommene Abstraktion der verschiedenen Schichten eines Denkmodelles das zur "idealen" Black Box führt. Führt man aber wie oben in die Schnittstelle = Interface wieder eine Schnittstelle auf den Implementor-> TMyInterfcae ein so durchbricht man diese strikte Logik und macht die Anwendung der Interfaces sinnnlos. Denn die Deklaration von IMyInterface oben bedeutet das diese Schnittstelle nur durch ein Objekt kompatibel zur Klasse TInterfacedObject durchgeführt werden kann. Diese Restriktion soll aber gerade mit Interfaces strikt vermieden werden. Ergo: ein normaler OOP Ansatz wie in meinem letzten Posting erreicht exakt das Gleiche wie der Vorschlag über die Interfaces. Nur mit dem Unterschied 1.) das es Typsicher ist und somit sauber, wir eben keinen harten und typ-unsicheren Cast verwenden wie in deinen Sourcen 2.) ohne Umwege funktioniert und somit zusätzliche Fehlerquellen beseitigt. Bedenke die schlimmsten Fehler liegen immer im Konzept eines Modelles ! 3.) kein Hilfsmittel misbraucht das für ganz andere Aufgaben gedacht ist, hier eben die Interfaces, und somit die spätere Verwenung deines Modelles für andere "korrekt denkende" Programmierer unmöglich macht. Denn für diese Programmierer ist es ein Unding wie du Interfaces mißbrauchen möchtest. Grundsätzlich wird man aber deine Problemstellung durch pure OOP und konzeptionelle Änderungen lösen können. Statt mit 2 Units zu arbeiten musst du OOP konform dein Klassenkonzept umstellen und mit 3 Unit arbeiten. 1. Unit -> Basisunit die eine abstrakte Vorfahrklasse enthält in der alle Gemeinsamkeiten der Nachfahrklassen deklariert wurden, meistens abstrakte Klassen. Sprich Klassen aus reiner Deklaration von abstrakten virtuellen Methoden ohne reale Implementierung. 2. Unit -> Nachfahre Class1 von der Basisklasse und Verwendung von UnitClass2 in der Implementation Sektion dieser Unit. 3. Unit -> Nachfahre Class2 von der Basisklasse und Verwendung von UnitClass1 in der Implementation Sektion dieser Unit. Zitat:
Gruß hagen |
Re: Zirkuläre Unit Reference
Zitat:
Defakto ist der Zeiger auf das Interface ein Zeiger mit festen Offset relativ zum Zeiger auf die implementierende Instance der Klasse. Nachzulesen in Unit TypInfo.pas. Implementiert eine Klasse in Delphi sogar mehere Schnittstellen unterschiedlichen Types so unterscheiden sich sogar all diese Zeiger dieser Interfaces !! Beispiel
Delphi-Quellcode:
Ergo: die Frage ist nicht "warum unzulässig" sondern "überhaupt möhglich ?", und nein ist es nicht und erzeugt nur AVs und sonst nichts. Du versuchst nämlich damit nur aus einem Auto die Farbe Grün zu machen ohne irgendwelche Überprüfungen ;)type IInterface1 = interface; IInterface2 = interface; TMyImpl = class(TInterfacedObject, IInterface1, IInterface2) var MyImpl: TMyImpl; MyInt1: IInterface1; MyInt2: IInterface2; begin MyImpl := TMyImpl.Create; MyInt1 := MyImpl as IInterface1; MyInt2 := MyImpl as IInterface2; if (Pointer(MyImpl) = Pointer(MyInt1)) then // ist FALSE if (Pointer(MyImpl) = Pointer(MyInt2)) then // ist FALSE if (Pointer(MyInt1) = Pointer(MyInt2)) then // ist FALSE // ergo: IInterface1(MyImpl).XYZ; // ist FALSCH IInterface2(MyImpl).XYZ; // ist FALSCH TMyImpl(MyInt1).XYZ; // ist FALSCH TMyImpl(MyInt2).XYZ; // ist FALSCH (MyImpl as IInterface1).XYZ; // ist RICHTIG, wenn IInterface1 eine GUID hätte, hat (MyImpl as IInterface2).XYZ; // ist RICHTIG, wenn IInterface2 eine GUID hätte, hat (MyInt1 as TMyImpl).XYZ; // ist FALSCH, da ein Interface keinen spzifischen Typ eines Implementor kennt, siehe Abstraktion oben (MyInt2 as TMyImpl).XYZ; // ist FALSCH, .... end; Gruß Hagen |
Re: Zirkuläre Unit Reference
Erst mal vielen Dank für deine ausführlichen Antworten. Habe wieder echt was gelernt.
Zitat:
Die Interfacelösung habe ich verworfen. Die Lösung über TObject habe ich ausprobiert und sie funktioniert sehr gut. :hello: Die 3 Klassen-Lösung muss ich mal Komplett durchdenken, ich bin mir noch nicht sicher ob ich mit so mein Problem lösen kann. |
Re: Zirkuläre Unit Reference
Zitat:
Der Aufwand über abstrakte Vorfahrklassen ist nicht unerheblich, das muß man fairerweise sagen, und eben auch öfters nicht immer sauber umsetzbar. Auch da können dann Typcasts vorkommen. Gruß Hagen |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:14 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