Home Computer Pascal Kurs Site Map counter

Ein Ausblick auf Delphi...

Delphi erscheint auf den ersten Blick kinderleicht, ja fast wie Programmieren mit LEGO Bausteinen, doch auch hier liegt die Schwierigkeit - Es wird Wissen über objektorientiertes Programmieren voraus gesetzt. Delphi ist so komplex, dass man einige Tausend Seiten darüber schreiben könnte. Ich will hier nur auf die Unterschiede zu Turbo Pascal und einige Anfängerschwierigkeiten eingehen.

Objekte - Was ist das?

Wenn man in Delphi ein neues Formular aufmacht, mit einem OK Button und einer Close Routine bekommt man folgendes:

Unit Unit1;
interface
Uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms,Dialogs;
type
    TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    private
    { Private declarations }
    public
    { Public declarations }
    end;
var
    Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
    Close;
end;
end.

Versuchen wir mal das zu erklären. Also zuerst einmal ist wichtig das oben im INTERFACE Teil ein Typ definiert wird. Unten im Implementierungsteil gibt es dann eine Variable dieses Typs. Wenn man sich die Deklaration ansieht so sieht es zuerst mal aus wie ein Record, mit dem Unterschied, das es neben Feldern (Button1) auch Prozeduren (Button1Click) gibt. Und in der Tat verhält sich dieses Objekt wie ein Record. So kommt man z.B. an die Datenfelder über den Punkt.

Der Unterschied liegt darin, das die Prozeduren und Funktionen mit den Datenfeldern verknüpft sind, sie bilden eine Einheit und erlauben es auf diese zuzugreifen. Anfangs ist man versucht wie bisher weiter zu arbeiten, und z.B. in seinem Quellcode neben den Objektroutinen auch normale (hier ohne TForm1.) zu verwenden. Das geht, nur haben diese nur über Referenzierung von FORM1, der Variablen Zugriff auf die Datenfelder. Sehr oft ist es sinnvoller allgemeine Routinen auszugliedern und eigene Routinen die Datenfelder dieses Objektes bearbeiten einzufügen.

Letzteres ist nicht so schwierig. Zuerst muss man sich entscheiden ob die Methode nur intern zur Verfügung steht (also innerhalb dieser Unit) oder auch nach außen, d.h. für andere Units die dieses Formular referenzieren. Dann gibt man im private / public Teil die Routine ein und fügt sie hinten im interface als Quelltext ein. Dies ist sehr ähnlich wie bei Units, nur muss man im Implementation Teil darauf achten das TFORM. Vorzustellen, damit Delphi auch weiß das diese Routine zum Objekt gehört. Ähnlich kann man das Objekt auch um eigene Datenfelder erweitern.

Ein Fehler den Anfänger machen ist das sie von außen auf Routinen über TForm.Button1Click(...) zugreifen wollen. TFORM1 ist aber als Platzhalter zu denken, denn TFORM1 wurde im Type Teil deklariert. Es ist praktisch die abstrakte Benennung, die bei einer Variablen durch die konkrete ersetzt wird. Hier heißt das Formular z.B. Form1, also wäre der richtige Name Form1.ButtonClick. Andere Units müssen immer die volle Bezeichnung nehmen, da sie anderes als die Objektroutinen nicht "im" Objekt sitzen. In der Fachwelt redet man von einem Objekt wenn man den Typ meint und von einer Instanz wenn man die Variable eines Objekttypen meint.

Dynamisch erzeugte Objekte

Im Handbuch wird erläutert wie man Stringlisten und Formulare von MDI Fenstern dynamisch erzeugt, d.h. zur Laufzeit. Man kann dies aber auch mit allen visuellen Komponenten machen wie Dialogen, Labels, Bildern...

Dazu ein paar Hinweise

In der Routine die eine Komponente dynamisch erzeugt geht man immer so vor

Procedure Mein_Formular.tuewas(Sender: TObject);
var opendia: topendialog;
begin
    opendia:=topendialog.create(self);
    try
        opendia.execute;
       ...
    finally
        opendia.free;
    end;
end;

Es ist hier eigentlich nur wichtig zu wissen wie es geht: Als erstes braucht man eine Variable des Typs das man anlegen will, also TLabel für ein Label oder TOpendialog für einen Datei Öffnen Dialog.

Bei fast allen visuellen Komponenten sieht der Konstruktor (Eine Prozedur die das Objekt erzeugt) gleich aus: nämlich Typname.create(self) das heißt erzeuge das Objekt und liefere einen Zeiger auf es selbst zurück. Self wird uns noch in diesem Zusammenhang begegnen. Jede Variable die wir so erzeugt haben müssen wir auch wieder freigeben und zwar mit der Prozedur free. Damit dies in jedem Fall erfolgt gibt es hier den try... finally Block. Ansonsten werden Methoden und Variablen des Objektes immer so angesprochen: Variablenname.methodenname. Also hier bei der Execute Methode opendia.execute und nicht topendialog.execute!

Es gibt hier nur zwei Fallstricke zu beachten. Erstens: Wenn man ein Objekt über eine Prozedur heraus benötigt sollte man es in Formcreate erzeugen und in Formclose wieder freigeben. Ob es schon existiert kann man abchecken indem mal auf Nil überprüft:

z.B.

if opendia<>nil then opendia.free;
oder:
if Assigned(opendia) then opendia.free;

Hier ist einer der wenigen Augenblicke wo sie merken, das sie eigentlich mit Zeigern arbeiten, und die sind nil wenn sie kein Objekt zugewiesen bekommen haben. Bei Formularen hat man als zweites oft das Problem das mit Show die Sache angezeigt wird und danach free aufgerufen würde, die bevor der Benutzer was machen kann das Formular wieder löscht. Hier setzt man die Variable closeaction in Formclose auf cafree und lässt dafür den Free Teil in der Try Routine weg.

Und nun noch ein paar wichtige Dinge für dynamisch erzeugte Formulare. Hier gibt es zwei Probleme: Zum einen die Referenzierung von anderen Komponenten und die Parameterübergabe.

Manchmal müssen Sie andere Komponenten im selben Formular referenzieren, so hat z.B. jede Komponente die Eigenschaft height und wenn Sie nun den Komponentennamen zum referenzieren brauchen so gibt es bei dem Zugriff auf Form1 z.B. einen Fehler wenn dieses dynamisch erzeugt wurde. Warum? Nun diese Variable gilt nur für Komponenten die einmal zu Anfang erzeugt wurden. Sie brauchten also den Variablennamen des aktuellen Formulars. Und das ist der Parameter self. Um also die Größe zu ändern setzen Sie self.height anstatt Form1.height.

Parameterübergaben: Nach dem Freigeben existieren alle Variablen des Objektes nicht mehr, was also machen wenn man Werte austauschen will. Nun es gibt hier keine allgemeine Lösung. Im Handbuch steht eine mit Zeigern auf Strings. Ich persönlich mache es so, das Formulare die andere aufrufen public Datenfelder haben, in die die dynamisch erzeugten Formulare ihre Ergebnisse schreiben. Somit sind die Datenfelder immer vorhanden.

Die Parameter is und as

Sehr bald kommt man als Anwender in die Situation, dass man verschiedenste Objekte ansprechen muss. Oftmals kann man sie über ihren Namen ansprechen doch manchmal wünscht man sich bessere Möglichkeiten. Diese gibt es auch. Über zwei Operatoren kann man den Typ eines Objektes ansprechen und umwandeln. Es sind is und as und man braucht sie meistens in Schleifen wo man in generischen Objekten arbeitet.

Ein Beispiel: Sie wollen in einer Schleife alle MDI Tochterformulare eines Typs schließen:

var x: tmeinform;
for i:=activmdicount-1 downto 0 do
if activemdichild[i] is Tmeinform then
begin
    x:=mdichild[i] as tmeinform;
    x.close;
end;

Mit is (variable is typ) stellen Sie fest ob diese Variable diesen Objekttyp hat. Die Funktion liefert true und false zurück. Mit as Typ können Sie dann eine Instanz dieses Typs erhalten. Im Beispiel z.B. ist das Array mdichildren vom Typ TForm, das ist der Vorfahr aller Fenster. Es wäre möglich gewesen close direkt aufzurufen, da diese Routine alle Fenster haben, aber hier sollte zu Demozwecken as zum Konvertieren benutzt werden.

Sie brauchen as immer wenn die Variable nicht direkt ihr Typ ist sondern ein Vorfahr. Das ist eine Eigenschaft von Objekten, sie stammen voneinander ab, und man kann aus einem generischen Feld, dass nur die Formulare hat, egal welche es sind, doch mittels as an den Typ herankommen den man will. Dazu muss man aber vorher mit is geprüft haben das dieser auch vorliegt, sonst gibt es Fehler.

Die beiden Operatoren brauchen sie immer wenn sie nicht auf Elemente über Namen sondern generisch über ihren Objekttyp zugreifen wollen. Beispielsweise wenn ein Drag & Drop Feld feststellen muss von wo hier was gedroppt wird und je nach Typ (Edit, Listenfeld) anders reagieren muss. Bei der Hilfe und im Handbuch ist hier ein Beispiel eines Dateimanagers drin der das verwendet. Sie brauchen sie auch wenn sie z.B. über Controls[i] auf alle Elemente eines Fensters zugreifen wollen um global die Farbe oder Schrift in allen Edit Feldern zu ändern. Dann müssen sie über is feststellen ob das Element ein Editfeld ist.

Neue Sprachmöglichkeiten

Auch abseits der visuellen Komponenten gibt es eine Reihe von interessanten Neuerungen. So kann man die Klasse TStrings und TStringList sehr geschickt einsetzen bei allem was man früher in Form von Textdateien gemacht hat. Sehr häufig muss man aus dieses Informationen extrahieren und hier ist es nützlich, das diese auch den gesamten Dateiinhalt in einem riesigen String - der Eigenschaft Text gespeichert haben.

Darüber hinaus ersetzen diese natürlich dynamisch angelegte String-Listen.

Dynamische Arrays gibt es ab Delphi4: Mit einer Deklaration ohne Index hat man ein dynamisches Array angelegt dass man mit Setlength dynamisch vergrößern und verkleinern kann! Das ganze geht auch mehrdimensional und man kann mit High und Low als Funktionen die Grenzen abfragen. Ich kenne keine andere Sprache in der es so einfach ist. Das Sprachkonstrukt ist in C und C++ mit erheblich mehr Arbeit verbunden. Das ist natürlich ideal für alle Anwendungen die mit variablen Datengrößen zu tun haben, aber eben nicht nur mit Texten (sonst könnte man TStringlist nehmen).

Wer mal in Sprachen ohne Typen gearbeitet hat wie SmallTalk oder Python, der ist manchmal neidisch, wenn er sieht das manches damit einfacher geht - In vielen Fällen weiß man ja was eine Variable für einen Sinn hat aber manchmal wäre es doch schön wenn sie den Typ wechseln kann. Das gibt es auch bei Delphi: Eine Variable vom Typ Variant kann z.B. einen Record, eine double Zahl oder ein String sein - und ihren Typ zur Laufzeit ändern. Trotzdem gibt es Möglichkeiten ihre Typ festzustellen. Ich rate hier mal in die Online Hilfe Variant einzutippen.

Default Parameter: Wie in C++ gibt es die Möglichkeit einen Wert im Prozedurkopf mit "=" anzuhängen der gesetzt wird, wenn eine Funktion mit weniger als der nominellen Parameterzahl aufgerufen wird. Eine Implementierung von MessageBox könnte z.B. so sein:

procedure MessBox (Msg: string; Caption: string 'Warning'; Flags: LongInt = mb_OK or mb_IconHand);
begin
    Application.MessageBox (PChar (Msg),
    PChar (Caption), Flags);
end;

und man kann sie auch so aufrufen:

MessBox ('Fehler im System!');
MessBox ('Fehler im System!', 'Fehler');
MessBox ('Hello World!', 'Hellop World Programm', mb_OK);

Variable Parameterlisten: Wer einmal über die Format Funktion gestolpert ist kennt deren komische Aufrufsyntax bei der Parameter in einem Array übergeben werden. Auch das ist etwas was es in der Form als Ellipse schon bei C gibt. Man kann hier noch variable Array Parameter (unterschiedliche Typen) oder einfache (von einem bestimmten Typ unterscheiden. Das Thema ist etwas komplexer und ich rate hier mal unter dem Stichwort Offene Array Parameter in die Hilfe zu schauen.

Ebenfalls von C++ abgeschaut sind überladene Prozeduren. Überladene Prozeduren sind Prozeduren mit demselben Namen aber unterschiedlichen Parameterlisten. Nehmen wir folgendes Beispiel:

Procedure Swap(Var A,B: Integer);

Diese Prozedur vertauscht zwei Integer Werte. Weil es VAR Parameter sind (was auch sonst?) geht das aber nicht mit double oder Strings, oder Records. Nun kann man natürlich eine SwapInt, SwapDouble... Prozedur definieren. Logischer ist es aber diese zu überladen:

Procedure Swap(Var A,B: Integer); overload;
Procedure Swap(Var A,B: Double); overload;
Procedure Swap(Var A,B: String); overload;

Der Compiler muss anhand der Parameter die Prozedur feststellen können die in Frage kommt, d.h. sie müssen sich in den Parametertypen unterscheiden. Es reicht nicht wenn eine Routine einen VAR, die andere einen CONST und die dritte einen Wertparameter desselben Typs hat - denn woher soll der Compiler wissen, was sie vorhaben?

Zum Thema Computer ist auch von mir ein Buch erschienen. "Computergeschichte(n)" beinhaltet, das was der Titel aussagt: einzelne Episoden aus der Frühzeit des PC. Es sind Episoden aus den Lebensläufen von Ed Roberts, Bill Gates, Steve Jobs, Stephen Wozniak, Gary Kildall, Adam Osborne, Jack Tramiel und Chuck Peddle und wie sie den PC schufen.

Das Buch wird abgerundet durch eine kurze Erklärung der Computertechnik vor dem PC, sowie einer Zusammenfassung was danach geschah, als die Claims abgesteckt waren. Ich habe versucht ein Buch zu schreiben, dass sie dahingehend von anderen Büchern abhebt, dass es nicht nur Geschichte erzählt sondern auch erklärt warum bestimmte Produkte erfolgreich waren, also auf die Technik eingeht.

Die 2014 erschienene zweite Auflage wurde aktualisiert und leicht erweitert. Die umfangreichste Änderung ist ein 60 Seiten starkes Kapitel über Seymour Cray und die von ihm entworfenen Supercomputer. Bedingt durch Preissenkungen bei Neuauflagen ist es mit 19,90 Euro trotz gestiegenem Umfang um 5 Euro billiger als die erste Auflage. Es ist auch als e-Book für 10,99 Euro erschienen.

Mehr über das Buch auf dieser eigenen Seite.

Hier geht's zur Gesamtübersicht meiner Bücher mit direkten Links zum BOD-Buchshop. Die Bücher sind aber auch direkt im Buchhandel bestellbar (da ich über sehr spezielle Themen schreibe, wird man sie wohl kaum in der Auslage finden) und sie sind natürlich in den gängigen Online-Plattformen wie Amazon, Libri, Buecher.de erhältlich.


© des Textes: Bernd Leitenberger. Jede Veröffentlichung dieses Textes im Ganzen oder in Auszügen darf nur mit Zustimmung des Urhebers erfolgen.
Sitemap Kontakt Impressum / Datenschutz Neues Hier werben / advertisment here Buchshop Bücher vom Autor Top 99