![]() |
Problem mit Uses-Klausel
Hallo zusammen
Entweder ist hier ein Bug oder ich hab etwas doch noch nicht verstanden: Ich habe mein Projekt in diverse Units aufgeteilt, um den Text übersichtlicher aufzuteilen. Nun war ich bisher der Meinung, verstanden zu haben, wie sich die Uses-Klausel im Interface bzw. Implementation-Teil verhalten. Nun ist aber ein Problem aufgetaucht, was mich an meinem Verständnis doch zweifeln lässt: Ich hatte bisher eine Unit, nennen wir sie 'Autos', in der waren die die Klasse 'Auto', sowie die einzelnen Klassen 'Ford', 'Audi' und 'VW' mit allem drum und dran deklariert. Nun wurde mir das zu unübersichtlich, und ich wollte 'Ford', 'Audi' und 'VW' in jeweils eigene Units verschieben, und sie im Interface-Teil unter Uses zu 'Auto' hinzufügen. Jetzt muniert aber die Unit 'Parkhaus', in die ich 'Auto' eingebunden habe, sie würde die Klassen 'Ford', 'Audi'... nicht finden. Haut und Schlagt mich, aber bitte erklärts mir nochmal... |
AW: Problem mit Uses-Klausel
In Parkhaus musst Du Ford und Audi auch nochmal mit aufnehmen, da Parkhaus sonst wirklich nur Auto kennt.
Bei komplexeren Verschachtelungen muss man darauf achten, dass im Delphi keine zirkulären units verwendet werden können. Wenn A auf B zugreift und B auf A dann muss eine der Bezüge im Implementationsteil definiert werden. Eine Möglichkeit, so etwas loser zu lösen sind Schnittstellen. Dann müssen die konkreten Klassen bzw. Units nicht unmittelbar bekannt sein. Die Verwendung schon Schnittstellen macht aber zusätzlichen Aufwand. Je nach Projekt und Erfahrungen kann man diese aber nutzen. EDIT: Oh! Lazarus! Dann muss das so nicht stimmen! |
AW: Problem mit Uses-Klausel
Hmm, das ist ja dann ungeschickt. Angenommen, jemand schreibt eine komplexere Komponente. Dann weiß der Endbenutzer doch gar nicht, was dafür alles verwendet wurde, sondern fügt auch nur die jeweilige Unit mit der Komponente ein. So hatte ich das eigentlich geplant, in C ging das immer. Mache ich hier nen MDF (Mega Denkfehler)?
|
AW: Problem mit Uses-Klausel
Bei eine Komponente gibt es auch noch die Möglichkeit bestimmte Units automatisch einbinden zu lassen. Das passiert aber zur DesignTime und wird von der IDE gesteuert.
Klatscht man eine Komponente auf die Form und kompiliert (oder speichert), dann wird die uses Liste auf einmal erweitert um die fehlenden units für diese Komponente(n). Ansonsten ja, da muss man wissen, welche Unit benötigt wird. |
AW: Problem mit Uses-Klausel
Es ist auch üblich, dass man nicht alles und jeden aus der Uses-Hierachie "vererbt". Das wäre ja ein heilloses Durcheinander im Scope der letztendlichen Programme! Normal ist, dass man seine Klassen/Komponenten so designed, dass man über diese und ihrer Unit alle nötigen Aufgaben mit ihr erledigen kann.
Edit: Okay, den Spezialfall den Sir Rufo nennt gibt es auch. Das habe ich bisher aber meist nur im Zusammenhang mit DBControls gesehen, und ich persönlich würde diese Praxis auf einem absolut notwendigen Minimum halten. Zumal die Uses-Klauseln dadurch alles andere als übersichtlicher würden. |
AW: Problem mit Uses-Klausel
Verstehe ich das richtig:
Units 'Lenkrad', 'Reifen', 'Felge', 'Getriebe', ... werden in unit 'Auto' benötigt. Wenn Ich jetzt 'Auto' benutzen möchte, dann muss ich auch alle dafür benötigten Unter-Units immer mit einbinden? |
AW: Problem mit Uses-Klausel
Die Unit 'Parkhaus' muss die Klassen 'Ford', 'Audi' und 'VW' normalerweise auch nicht kennen.
Alle für Parkhaus benötigten Methoden und Eigenschaften sollten schon in der Basisklasse "Auto" deklariert sein. Die Implementierung der Methoden und die Werte der Eigenschaften können dann für jede abgeleitete Klasse unterschiedlich sein. Wenn du auf Eigenschaften von Auto zugreifen möchtest deren Typen in 'Lenkrad', 'Reifen', 'Felge', 'Getriebe' deklariert sind, müssen die jeweiligen Units auch eingebunden werden. |
AW: Problem mit Uses-Klausel
Nein, nur wenn Du auf aAuto.Lenkrad.Durchmesser zugreifen willst.
In dem Fall musst Du wissen, was ein Lenkrad ist und welche Eigenschaften das hat. Alternativ kannst Du dem Auto aber auch eine Schnittstelle hinzufügen: aAuto.Lenkraddurchmesser. Das könnte z.B. eine Funktion sein, die dann (intern) auf das Objekt TLenkrad zugreift. |
AW: Problem mit Uses-Klausel
Aaaah, ok jetzt fällt die Rupie. Dh wenn ich zur Klasse 'Auto' ne Funktion 'GetLenkradDurchmesser' einfüge, dann kann ich das "Tunneln". Danke
|
AW: Problem mit Uses-Klausel
Also so ganz ist das ja nicht :)
Delphi-Quellcode:
unit AutoTeile;
interface type TLenkrad = class property Durchmesser : integer; end;
Delphi-Quellcode:
unit Auto;
interface uses AutoTeile; type TAuto = class property Lenkrad : TLenkrad; end;
Delphi-Quellcode:
unit MyMainUnit;
interface uses Auto; // kennt nur Auto var MeinAuto : TAuto; // und doch geht der Zugriff auf MeinAuto.Lenkrad.Durchmesser |
AW: Problem mit Uses-Klausel
Zitat:
Und der TE fragte auch ganz "speziell" nach Komponenten. (ist aber in diesem Zusammenhang aber eher nur eine Randnotiz wert, weil es eben IDE "speziell" ist) |
AW: Problem mit Uses-Klausel
Das ja, was aber nicht ginge ist:
Delphi-Quellcode:
Da die Deklaration von TLenkrad fehlt. Memberzugriff geht immer, auch ohne das der Typ des Objekt-Feldes im Scope liegt. Nur die unmittelbare Verwendung dessen geht nicht. Ebenso geht auch z.B. das nicht:
unit MyMainUnit;
interface uses Auto; // kennt nur Auto var MeinAuto : TAuto; MeinAuto.Lenkrad := TLenkrad.Create;
Delphi-Quellcode:
Um das zu realisieren gibt es mehrere Wege: Eine Factory, die eine Enummeration als Parameter erhält und anhand dessen ein Objekt des richtigen Typs liefert; das gleiche nur anders: In der Unit Auto müssten dann noch Dinge wie "TMercedesClass = class of TMercedes;" usw. deklariert werden, welche statt der Enummeration übergeben würden; eine String-Basierte Factory die einen Text-Parameter nimmt und anhand diesem unterscheidet, ... letztlich würde hier, wenn es um das Erzeugen eines spezifischen Autos geht, am ehesten aufs Factory-Pattern hinauslaufen. Fühlt sich für mich zumindest am "natürlichsten" an. Wie man dieser nun konkret sagt, was sie erzeugen soll, wäre dann wie o.g. sehr variabel. Da käme es dann auf den genauen Einsatz an, und wohl auch persönlichen Geschmack.
uses
Auto; // kennt nur Auto var MeinAuto : TAuto; MeinAuto := TMercedes.Create; @Roter-Kasten-Rufo: Dass die VCL nicht der Gold-Standard der Best-Practices ist, weiss ich auch ;P |
AW: Problem mit Uses-Klausel
@Medium :thumb:
|
AW: Problem mit Uses-Klausel
Irgendwas kam mir doch schon beim Abschicken merkwürdig vor. :oops:
Hätte auf meine Signatur hören sollen. :stupid: |
AW: Problem mit Uses-Klausel
Hmm nächstes Problem: meine 'TParkhaus' enthält eine 'Autos : TList', die nur die Pointer auf die verwendeten 'TAuto' enthält. Ich habe zwar eine procedure Parthaus.AddAuto(Name : String), und eine function Parkhaus.GetAuto : TAuto, allerdings kennt UnitMain ja nur 'Parkhaus' und kann dann zwar ein Auto hinzufügen, aber nichts mit dem ReturnValue von GetAuto vom Typ TAuto anfangen, richtig?
|
AW: Problem mit Uses-Klausel
Richtig.
|
AW: Problem mit Uses-Klausel
Ich glaube, die Rupie hängt noch knapp über dem Boden fest:
Wenn 'TParkhaus' eine Property vom Typ 'TAuto' hat, dann kennt die MainUnit, die TParkhaus verwendet auch TAuto. Ist das richtig? Dann könnte ich anstatt der GetAuto ja auch eine ArrayProperty mit Typ TAuto benutzen, die den entsprechenden Zeiger aus der TList zurückgibt. Kennt die MainUnit dann TAuto? |
AW: Problem mit Uses-Klausel
Damit die MainUnit die Klasse TAuto kennt, muss diese in irgendeiner der eingebundenen Units auch deklariert sein.
|
AW: Problem mit Uses-Klausel
Ok danke, ich denke ich habs. Ich bin noch von meiner Arbeit mit C ausgegangen, wo ein #include den gesamten damit eingebundenen Text inklusive aller dort vorhandenen #includes kennt.
|
AW: Problem mit Uses-Klausel
Wichtiger Unterschied zu Includes: Diese werden quasi wie einkopierter Text behandelt (imho werden sie sogar genau das), während unter Units ein "semantischer" Zusammenhang besteht: Lediglich das, was in einer Unit explizit an Typen deklariert ist, ist nach aussen hin sichtbar, und die Sichtbarkeit erstreckt sich auch nur über genau eine Uses-Klausel, und wird über diese nicht an weitere "Unterbenutzer" weitergeleitet.
Delphi kennt unterdessen auch Includes. Hier aber nicht über ein Keyword, sondern eine Compilerdirektive ({$i Dateiname} wenn ich nicht irre, grad kein Delphi parat). Dabei wird dann ebenfalls nur der Inhalt von "Dateiname" an die Stelle kopiert, wo die Direktive steht. Ist keine gängige Praxis, und imho auch kein allzu astreiner Stil, in manchen Situationen aber sehr hilfreich. Bei dir bin ich aber der festen Überzeugung, dass sich das auch über ein geeignetes Klassendesign schöner lösen lässt. |
AW: Problem mit Uses-Klausel
Zitat:
Eine Frage hab ich dazu noch: Ich habe also
Delphi-Quellcode:
Demnach geht {1} natürlich nicht, da UnitMain TAuto nicht kennt, wie vorhin schon klargestellt. Aber geht {2}?
uses UnitAuto;
type Parkhaus = class FAuto : TList; function GetAuto(Index : Integer) : TAuto; ... property Auto[index : integer] : TAuto read GetAuto; end; //In der UnitMain uses UnitParkhaus; ... {1} function meinAuto : TAuto; begin result := Parkhaus.Auto[0]; end; {2} function AnzahlReifen : Integer; begin result := Parkhaus.Auto[0].NReifen; end; Oder braucht es dazu eine 'function TParkhaus.GetNReifen(Index) : integer'? |
AW: Problem mit Uses-Klausel
Wenn die Funktion TAuto zurückgibt, muss TAuto auch bekannt sein, sonst könnte die MainUnit ja gar nicht wissen, dass diese über eine Methode/Property NReifen verfügt.
|
AW: Problem mit Uses-Klausel
Zitat:
|
AW: Problem mit Uses-Klausel
Sicher? Ich hab gerade kein Delphi am Start, aber es würde mich wundern.
|
AW: Problem mit Uses-Klausel
Zitat:
|
AW: Problem mit Uses-Klausel
Ich bin fest davon überzeugt, dass philharmonys Annahmen völlig korrekt sind, und man keinen Getter für 2 bräuchte. Wenn das nicht wäre, würde man ja sowas von übel im Klassendesign beschränkt sein, und jede Pupel-Property durchreichen müssen. Man kann eben nur die Instanz von Parkhaus.Auto[i] keiner Variablen zuweisen, da für diese kein passender Typ verfügbar wäre.*
*)Wäre es schon: Jede Elternklasse von TAuto ginge, nur könnte man nachher nicht mehr dort hin casten um an die Member heranzukommen die seit dem verwendeten Typ dazu kamen. Und da alle Klassen zwangsweise von TObject erben, ginge durchaus dieses:
Delphi-Quellcode:
Nur könnte man nicht mehr auf
var
auto: TObject; begin auto := Parkhaus.Autos[0];
Delphi-Quellcode:
zugreifen, da es das in TObject noch nicht gibt. Gäbe es hingegen einen Vorfahren von TAuto - z.B. TBereiftesVehikel - der bereits den Member NReifen kennt, und man dessen Unit einbände, DANN ginge das wieder. Aber das sind jetzt langsam eigentlich nur noch theoretische Überlegungen.
auto.NReifen
|
AW: Problem mit Uses-Klausel
Zitat:
Delphi-Quellcode:
zusammen mit dem richtigen Include-Verzeichnis als Compilerparameter reicht hier :mrgreen:
{$include platformspecific.inc}
Und zudem bietet die Lazarus IDE auch eine bessere Unterstützung für Includes (eben genau wegen der oben genannten gängigen Praxis) als es Delphi hat. Gruß, Sven |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:09 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