Delphi-PRAXiS
Seite 1 von 4  1 23     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Meine Probleme mit Delphi-OOP ... (https://www.delphipraxis.net/166632-meine-probleme-mit-delphi-oop.html)

trebor90 22. Feb 2012 14:14

Meine Probleme mit Delphi-OOP ...
 
Hallo alle miteinander,

zur Historie
Ich begann in der Schule Delphi zu lernen, wie man das eben so macht; tippeln, verstehen, freuen :-D
Dann habe ich C, Java, C++ im Zuge des Studiums gelernt und kann jetzt OOP *freu* :-D
Nun kehre ich zu Delphi zurueck und gerate ganz schoen ins Straucheln ... :cry:


Meine Probleme
Ich habe also in C++ intensiver Klassengestaltung gelernt; ebenso virtuelle und abstrakte Methoden.
Aufgrund dessen kollidiere ich in Delphi mit den Worten >Verdecken/Verbergen< und >Ueberschreiben<.
Darueber hinaus programmiere ich ja auch in der Delphi IDE und diese erzeugt ja schonmal vorab eine ganze Menge Quelltext. Umso mehr knallt's dann, wenn ich versuche ein halbwegs vernuenftiges Klassenkonzept zu realisieren, ohne komplett in der vorgefertigten Kiste "herumzupfuschen" ...


Jetzt ganz genau
1.) Was ist der Unterschied zwischen Verbergen/Verdecken und Ueberschreiben? Ich habe widerspruechliche Dinge gelesen, nach welchen die verdeckten Methoden wieder mit inherited sichtbar gemacht werden koennen ...

2.) Ich habe Zwei Delphi-Formen, wobei ich die erste mit einem eigenen Konstruktor Create(Eigentuemer: TComponent; Kanalnummer: Integer) erzeuge und die Meldung bekomme:
[Warnung] Unit2.pas(25): Methode 'Create' verbirgt virtuelle Methode vom Basistyp 'TCustomForm'
Dabei hat meiner aber eine andere Signatur als der originaere --> Create(AOwner: TComponent)
Was soll ich tun (Bitte nicht nur sagen: reintroduce), ich will ihn ja eigentlich ueberladen, aber mit overload verschwindet die Warnung auch nicht, und Warnungen ignoriere ich nicht.

3.) Ist es ueberhaupt moeglich, ohne den von Delphi automatisch erzeugten Quelltext umzubauen, vernuenftig und streng objektorientiert zu programmieren? Es geht dabei primaer um GUIs.
Ich habe zum Beispiel erstmal die Klassen umbenannt, Zugriffspezifizierer gesetzt, globale Variablen fuer die Forms entfernt (>Form1: TForm1<), Konstruktoren geschrieben, automatische Erzeugungen (FormCreate) entfernt ...
Sieht jetzt so aus:

Delphi-Quellcode:
program Gehoerbildung;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Hauptfenster},
  Unit2 in 'Unit2.pas' {GeraeteDialog};

{$R *.RES}

var Hauptfenster: THauptfenster;
begin
  Hauptfenster := THauptfenster.Create(nil);
   Application.Initialize;
   Application.CreateForm(THauptfenster, Hauptfenster);
  Application.Run;
end.
Delphi-Quellcode:
unit Unit1;

interface

uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,
   Buttons, MidiOut, Unit2;

type
  TTon = array [1..5] of Integer;
   THauptfenster = class(TForm)
   published
    AbsoluterTonCheckBox: TCheckBox;
    ZweiToeneCheckBox: TCheckBox;
    DreiToeneCheckBox: TCheckBox;
    VierklangCheckBox: TCheckBox;
    FuenfklangCheckBox: TCheckBox;
    HoheLageCheckBox: TCheckBox;
    MittlereLageCheckBox: TCheckBox;
      TiefeLageCheckBox: TCheckBox;
      GenugButton: TButton;
      MIDIDialogButton: TButton;
      WiederholungButton: TBitBtn;
      NeuerVersuchButton: TBitBtn;
      AufloesungButton: TBitBtn;
      CrashButton: TBitBtn;
      GehoerbildungLabel: TLabel;
      KleinC: TLabel;
      GrossC: TLabel;
      CEins: TLabel;
      CZwei: TLabel;
      CDrei: TLabel;
      Trennlinie1: TShape;
      Trennlinie2: TShape;
      Shape1: TShape;

   private
      FToene: TTon;
      FGeraeteDialog: TGeraeteDialog;
      FMidiOut: TMidiOutput;
      property GeraeteDialog: TGeraeteDialog read FGeraeteDialog write FGeraeteDialog;

   published
      procedure NeuerVersuchButtonClick(Sender: TObject);
      procedure WiederholungButtonClick(Sender: TObject);
      procedure AufloesungButtonClick(Sender: TObject);
      procedure CrashButtonClick(Sender: TObject);
      procedure GenugButtonClick(Sender: TObject);
      procedure MIDIDialogButtonClick(Sender: TObject);

      procedure FormCreate(Sender: TObject);
      procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
      procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
      procedure FormDestroy(Sender: TObject);

      procedure ErzeugeKlang(AnzahlToene: Integer);
      procedure BestimmeTiefstUndHoechstton(var Tiefster: Integer; var Hoechster: Integer);

   public
      function GetTon(Stelle: Integer): Integer;
      procedure SetTon(Stelle, NeuerTon: Integer);
      property Toene[Stelle: Integer]: Integer read GetTon write SetTon;
      property MidiOut: TMidiOutput read FMidiOut write FMidiOut;
   end;

implementation
--> Hier fehlen die globalen Variablen ...


4.) Wie verwendet man die Zugriffsspezifizierer (>Sichtbarkeitsattribute<) richtig? Musste zwangslaeufig das Meiste in published-Bloecke tun, dass das Programm ueberhaupt uebersetzt wird bzw. nicht abbricht aufgrund fehlender Klassenbestandteile. Aber das ist doch doof - steht doch total im Widerspruch zu Datenkapselung.


Nachtrag
Ich habe mich hier schon durch ein Paar Themen gelesen (mittels der Suche), ebenso durch mehrere Tutorien.
Doch habe ich's immer noch nicht ganz begriffen.
Bitte versteht also, wenn ich keine weiteren Verweise darauf suche, sondern es noch mal mit euren Worten erklaert bekommen moechte. Das bezieht sich insbesondere auf die Zugriffsspezifizierer und Ueberladen etc. .



Bis zur Hilfe,
Ri

himitsu 22. Feb 2012 14:24

AW: Meine Probleme mit Delphi-OOP ...
 
Das Überschreiben und Verdecken sollte es aber auch im C++ geben. (auch wenn dort die Syntax vielleicht etwas anders ist)

Inherited hat damit erstmal grundsätzlich garnichts zu tun.
Der Aufruf von inherited besagt nur, daß man an dieser Stelle die Methode eines Vorfahren und nicht eine "Eigene" aufrufen will
und das geht sowohl bei überdeckten, als auch bei überschriebenen Methoden.

Delphi-Quellcode:
type
  TMyClassA = class
    procedure A; overload; // überladen
    procedure A(i: Integer); overload;
    procedure X;
    procedure Y; virtual; // mit oder ohne abstract
    procedure Z; virtual; // mit oder ohne abstract
  end;

  TMyClassB = class(TMyClassA)
    procedure X; // verdeckt
    procedure Y; // verdeckt
    procedure Z; override; // überschrieben
  end;

var
  C: TMyClassA;
  D: TMyClassA;
  E: TMyClassB;

C := TMyClassB.Create;

C.Y; // TMyClassA.Y wird ausgeführt, da die Methode nur verdeckt, aber nicht überschrieben wurde.
C.Z; // TMyClassB.Z wird ausgeführt

C.Free;

C := TMyClassA.Create;
D := TMyClassB.Create;
E := TMyClassB.Create;

C.Y; // TMyClassA.Y
C.Z; // TMyClassA.Z

D.Y; // TMyClassA.Y
D.Z; // TMyClassB.Z

E.Y; // TMyClassB.Y
E.Z; // TMyClassB.Z

DeddyH 22. Feb 2012 14:31

AW: Meine Probleme mit Delphi-OOP ...
 
Und zum published: der Abschnitt ist für die Kommunikation mit dem Objektinspektor gedacht. Also muss folglich alles, was man bereits zur Designtime zuweisen will, in diesen Abschnitt. Außerdem werden Komponenten, die hier stehen, in die *.dfm aufgenommen und aus ihr geladen. Wenn man auf diesen Komfort verzichten kann/will, dann kann man sie auch z.B. in den private-Abschnitt verschieben, muss sie dann aber auch zur Laufzeit erzeugen/freigeben und alle Werte per Code zuweisen (inkl. der Events).

shmia 22. Feb 2012 14:50

AW: Meine Probleme mit Delphi-OOP ...
 
Ich geb Dir mal eine Teilantwort auf deine Fragen.
Also der Konstruktor von TComponent ist virtuell.
Damit ist auch der Konstruktor aller davon abgeleiteten Klassen (insbesondere TForm) auch virtuell.
Sobald ein virtueller Konstruktor existiert, ändern sich die Spielregeln für die Klassen (und alle davon abgeleiteten Klassen).
Wenn man nun einen anderen Konstruktor als den vordefinierten virtuellen Konstruktor benützt bekommt man das Problem dass der selbstdefinierte Konstruktor unter Umständen nicht aufgerufen wird.
Delphi-Quellcode:
// Beispielcode
// Hier wird ein Formular erzeugt aber dabei nur der virtuelle Standardkonstruktor aufgerufen
Application.CreateForm(TForm, Form1);
Daher gilt die Regel:
keine neuen Konstruktoren für Klassen definieren, die von TComponent abgeleitet sind!!
Nur diese Signatur ist erlaubt
Delphi-Quellcode:
public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

himitsu 22. Feb 2012 16:44

AW: Meine Probleme mit Delphi-OOP ...
 
Zitat:

Zitat von DeddyH (Beitrag 1152365)
Und zum published: der Abschnitt ist für die Kommunikation mit dem Objektinspektor gedacht.

Zum Beispiel mit dem OI. Nicht nur für diesen. :angle:
Published legt erweiterte RTTI-Informationen an.

Nja, will man Designtime-Komponenten haben, muß man von TComponent erben.
TComponent ist im Modus {$M+} compiliert, womit "published" als Standardabschnitt aktiv ist. (wenn vorher kein private, public und Co. angegeben wurde, so wie in meinen Beispielklassen)
Ansonsten ist alles standardmäßig public.

trebor90 22. Feb 2012 17:29

AW: Meine Probleme mit Delphi-OOP ...
 
Hallo und danke erst einmal fuer die raschen Antworten.

Also zuersteinmal zum Verdecken und Ueberladen
Ich verstehe es nicht. Auch anhand des Beispiels nicht. Besser waere eine exakte Definition von Verbergen und Ueberschreiben im Zusammenhang mit Virtual (und ohne dieses).
Wir haben in C++ gelernt, dass es im Zuge der Vererbung zwei Moeglichkeiten gibt, sich auf eine bereits definierte Methode zu beziehen:
  • Ueberladen - Definition einer Methode mit bereits vorhandenem Namen aber anderer Signatur
  • Ueberschreiben - Definition einer bereits vorhandenen Methode (die ueberschriebene steht dann noch immer zur Verfuegung) - Dabei ist virtual NICHT ZWINGEND
Virtual wird erst erforderlich, wenn dynamisches Binden gewuenscht ist, also erst zur Laufzeit entschieden wird, welche der vielen "gleichen" Methoden ausgefuehrt werden soll (aber auch nur bei Zeigern und Referenzen). Ohne virtual wird ansonsten immer die Methode des Datentyps ausgefuehrt, welche die das Objekt beinhaltende Variable hat, unabhaengig davon, welchen Datentyp das Objekt in Wirklichkeit hat ...
Hier scheint das naders zu sein: Hier muss zum Ueberladen wohl immer die Basismethode virtual sein ...
--> Dringender Erklaerungsbedarf zu dem ganzen Komplex (in Form einer sauberen, klar abgegrenzten Definition)


Zum Published
Ok, das mit dem Objektinspektor wusste ich.
Heisst das aber, dass auch alle Methoden, die durch die Form-Komponenten benutzt werden (Ereignisbehandlungsroutinen, z. B. ButtonClick, EditKeydown ...) auch published sein muessen?? Bzw. Methoden, welche von dieses Ereignisbehandlungsroutinen benutzt werden??? Das waere ja ein Schlag in die Magengrube der Objektorientierung ...
So waeren ja nur noch Felder und Methoden geschuetzt, welche nichts mit der Form zu tun haetten.
Ist das richtig?
(Ausgangspunkt ist natuerlich, dass ich mit dem Objektinspektor arbeiten will)


Zum Virtuellen Konstruktor
Also dass mein eigens entwickelter Konstruktor nicht aufgerufen wird, halte ich fuer unwahrscheinlich.
Ich rufe ihn ja selbst explizit auf. In einer anderen Form. Also meine erste Form erzeugt die zweite mit dem selbstentwickelten Konstruktor. Dabei entsteht die Warnung. Auch bei overload (da ich ihn ja ueberlade).
Aehnlich verfahre ich auch mit der ersten Form: Dafuer erstelle ich im Hauptprogramm eine lokale Variable, welche ich mit einem neuen Form-Objekt durch den Standardkonstruktor Create(AOwner: TComponent) initialisiere; danach kommt erst CreateForm(...).
Bitte dafuer noch einmal meine Ueberarbeitung des Hauptprogramms im ersten Beitrag ansehen.
Habe wie gesagt ALLE globalen Variablen (*Gaensehaut bekomm*) entfernt und eine lokale Variable fuer das Hauptprogramm angelegt.

Aber zu der Aussage, die du triffst, >shmia<:
Dann koennte ich ja alle Felder, die ich einer Form hinzufuege nur von aussen "nach-initialisieren", mit Set-Methoden, denn Konstruktoren darf ich laut deiner Aussage dafuer nicht schreiben.
Auch das waere ein Verstoss gegen das Prinzip der OOP. Jedes Objekt soll sich selbst verwalten und die Konsistenz der eigenen Daten sicherstellen.


Entschuldigt, wenn ich rechthaberisch klinge, aber genau deswegen eroeffnete ich das Thema, weil es momentan heftige Kollisionen mit meinem bisherigen Verstaendnis der OOP (aus anderen Sprachen) gibt.



Bis dann,
Ri

shmia 22. Feb 2012 18:09

AW: Meine Probleme mit Delphi-OOP ...
 
Zitat:

Zitat von trebor90 (Beitrag 1152384)
Zum Virtuellen Konstruktor
Also dass mein eigens entwickelter Konstruktor nicht aufgerufen wird, halte ich fuer unwahrscheinlich. Ich rufe ihn ja selbst explizit auf.

Jede Komponente kann aber auch von der VCL erzeugt werden.
Dieser Weg wird in der Praxis weit häufiger benützt als das explizite Aufrufen des Konstruktors.
Jedes Label, Editfeld, Menü,... wird ja normalerweise unter Zuhilfenahme der DFM-Datei dynamisch zur Laufzeit erzeugt.
Genau aus diesem Grund wurde ja für Komponenten ein virtueller (Standard-)Konstruktor eingeführt.
Wenn deine Komponente eine weiteren Konstruktor hat, dann wird dieser von der VCL ignoriert.
Daraus kann man nur die Schlussfolgerung ziehen, dass es einfacher und sicherer ist Parameter nicht über den Konstruktor sondern über Properties dem Objekt bekannt zu machen.
Delphi-Quellcode:
// Negativbeispiel
TDoppeltGemoppelt = class(TBasisComponent)
private
  FAnzahl : integer;
public
  // virtueller Standard Konstruktor
  constructor Create(AOwner:TComnponent); override;

  // nicht-standard Konstruktor
  constructor Create(anzahl:integer);
published
  property Anzahl:integer read FAnzahl write FAnzahl default 100;
end;


constructor TDoppeltGemoppelt.Create(AOwner:TComnponent); override;
begin
  inherited; // vererbten Konstruktor aufrufen
  FAnzahl := 100; // Defaultwert für property Anzahl setzen
end;

constructor TDoppeltGemoppelt.Create(anzahl:integer);
begin
  // wir müssen auf jeden Fall den Standard Konstruktor aufrufen
  // wenn das vergessen wird, gibt es Probleme
  Create(nil {Owner ist hier unbekannt});
  FAnzahl := anzahl;
end;

.....

var
  a, b : TDoppelGemoppelt;
begin
  a := TDoppelGemoppelt.Create(42);

  b:= TDoppelGemoppelt.Create(nil);
  b.Anzahl := 42;
Um das Objekt "b" zu initialisieren benötigt man eine Zeile mehr als für Objekt "a".
Wenn aber der eigene Konstruktor so wenig Zusatznutzen bringt ist es besser ihn ganz weg zu lassen und immer nur mit dem Standard Konstruktor und Properties zu arbeiten.

Komponenten in Delphi sind in der Hinsicht genau gleich wie JavaBeans.

MGC 22. Feb 2012 21:45

AW: Meine Probleme mit Delphi-OOP ...
 
Zitat:

Zitat von trebor90 (Beitrag 1152384)
Hallo und danke erst einmal fuer die raschen Antworten.
Dabei entsteht die Warnung. Auch bei overload (da ich ihn ja ueberlade).

An genau dieser Stelle hast Du die oben aufgeführten Beispiele nicht genau angesehen. Das Überladen von Funktionen und Prozeduren, den sogenannten Methoden eines Objekts wird im Deklarationsteil des Objekts abgegeben und unterstützt die Polymorphie. Overload darf nicht mit dem Überschreiben eines Konstruktors oder Destruktors, bzw. jeder anderen virtuellen Methode verwechselt werden. Beim Überladen wird eine weitere oder auch mehrere weitere Methode/Methoden eingeführt, die eine unterschiedliche Parameterliste aufweisen.

Siehe auch bei Delphi-Treff folgendes:
Zitat:

[...]
Prozeduren und Funktionen überladen

In Delphi ist es möglich, im selben Gültigkeitsbereich mehrere Routinen mit identischem Namen zu deklarieren. Dieses Verfahren wird "Überladen" genannt.

Überladene Routinen müssen mit der Direktiven overload deklariert werden und unterschiedliche Parameterlisten haben.

Delphi-Quellcode:
  function Divide(x, y: real): real; overload;
  begin
    result := x / y;
  end;

  function Divide(x, y: integer): integer; overload;
  begin
    result := x div y;
  end;
Diese Deklarationen definieren zwei Funktionen namens Divide, die Parameter unterschiedlicher Typen entgegennehmen. Wenn Divide aufgerufen wird, ermittelt der Compiler die zu verwendende Funktion durch Prüfung des übergebenen Parametertyps.

Divide(6.0, 3.0) ruft beispielsweise die erste Divide-Funktion auf, da es sich bei den Argumenten um reelle Zahlen handelt, auch wenn der Nachkommateil Null ist.

Überladene Methoden müssen sich deshalb entweder in der Anzahl der Parameter oder in den Typen dieser Parameter signifikant unterscheiden.
Wenn Du allerdings eine virtuelle Methode, bzw. einen Konstruktor oder Destruktor überschreiben möchtest, so verwendest Du den Befehl OVERRIDE.
Um sicherzustellen das dennoch Dein Objekt erzeugt wird, rufst Du die ursprüngliche Methode mit inherited auf. Also beim Konstruktor inherited aufrufen und dann Deinen eigenen Code abarbeiten lassen. Beim Destruktor zuerst Deinen eigenen Code ablaufen lassen udn inherited zum Schluss (Erklärt sich von selbst wenn man bedenkt, dass man in nicht vorhandenen Objekten keine Änderungen durchführen kann).

Delphi-Quellcode:
TBasisClass = TObject
  constructor Create(AOwnder: TComponent); virtual;
  destructor Destroy; virtual;
end;

TMyClass = TBasisClass
  constructor Create(AOwner: TComponent); override;
  destructor Destroy; override;
  function DoSomeThing(Left,Right: Integer): Boolean; Overload;
  function DoSomeThing(Left,Right: String; Up,Down: Integer): Boolean; Overload;
end;

constructor TMyClass.Create(AOwner: TComponent);
begin
  inherited;
  { Eigebner Quellcode der abgearbeitet werden soll }
end;

destructor TMyClass.Destroy;
begin
  { eigener Code der abgearbeitet werden soll }
  inherited;
end;

function TMyClass.DoSomeThing(Left,Right: Integer): Boolean;
begin
  { DoSomeThing }
end;

function TMyClass.DoSomeThing(Left,Right: String; Up,Down: Integer): Boolean;
begin
  {DoSomeThingElse }
end;

trebor90 23. Feb 2012 13:28

AW: Meine Probleme mit Delphi-OOP ...
 
Warum glaubt mir denn keiner ...?? :cry:

Das, was ihr sagt, habe ich doch weiter oben alles selbst schon beschrieben ...
Ich weiss, was Ueberladen und Ueberschreiben ist ...

Zitat:

Zitat von trebor90
Wir haben in C++ gelernt, dass es im Zuge der Vererbung zwei Moeglichkeiten gibt, sich auf eine bereits definierte Methode zu beziehen:

Ueberladen - Definition einer Methode mit bereits vorhandenem Namen aber anderer Signatur
Ueberschreiben - Definition einer bereits vorhandenen Methode (die ueberschriebene steht dann noch immer zur Verfuegung) - Dabei ist virtual NICHT ZWINGEND

Zitat:

Zitat von MGC
Wenn Du allerdings eine virtuelle Methode, bzw. einen Konstruktor oder Destruktor überschreiben möchtest, so verwendest Du den Befehl OVERRIDE.

--> Will ich NICHT. Ich will ihn ueberladen, eine weitere Variante des Konstruktors der Form hinzufuegen ...

Delphi-Quellcode:
constructor Create(Eigentuemer: TComponent; DieKanalnummer: Integer); overload;

constructor TGeraeteDialog.Create(Eigentuemer: TComponent; DieKanalnummer: Integer);
begin
   inherited Create(Eigentuemer);
   Kanalnummer := DieKanalnummer;
end;
--> Das ist, was ich will. Und die Warnung bleibt bestehen, obwohl ich nix verdecken will, nur ueberladen.

Zitat:

Zitat von shmia
Wenn deine Komponente eine weiteren Konstruktor hat, dann wird dieser von der VCL ignoriert.

--> Das denke ich nicht. Ich stelle nochmal mein Hauptprogramm ein:

Delphi-Quellcode:
var Hauptfenster: THauptfenster; //globale Variable in Unit1 geloescht!!!!! Hier LOKALE Variable angelegt
begin
  Hauptfenster := THauptfenster.Create(nil); //selbststaendig den Standardkonstruktor der ersten Klasse eingestellt
   Application.Initialize;
   Application.CreateForm(THauptfenster, Hauptfenster); //Hier wird die erste Form durch VCL initialiserst/virtueller Standard-Konstuktor
   //Hier stuende noch eine Zeile, wo die zweite Form initialisiert wuerde, aber die habe ich geloescht, denn das macht mein eigener explizit aufgerufener Konstruktor in der ersten Form; die zweite Form ist ein Attribut der ersten Form
  Application.Run;
end.
Vielleicht nochmal zum Verstaendnis:
Mein Programm besteht aus zwei Forms, eine wird im Hauptprogramm durch die VCL initialisert (wie oben zu sehen), die andere wird als Attribut der ersten mit einem expliziten Konstruktor UND NICHT DURCH DIE VCL erstellt. Dabei entsteht die Warnung.

himitsu 23. Feb 2012 13:48

AW: Meine Probleme mit Delphi-OOP ...
 
Delphi-Quellcode:
Hauptfenster := THauptfenster.Create(nil); // hier erstellst du manuell eine Instanz von TForm.
Application.Initialize; // und daß Erstellen auch noch vor dem Initialisieren der VCL
Application.CreateForm(THauptfenster, Hauptfenster); // und hier wird nochmal ein eine Instanz erstellt.
PS: Jetzt steht in Hauptfenster der Instanzzeiger der automatisch erstellten Instanz, aber (da Visible der Formulare standardmäßig) False ist, wird hier eventuell nur das zuerst erstellte Fenster angezeigt und nicht das jenes, welches in der Variable gespeichert ist.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:22 Uhr.
Seite 1 von 4  1 23     Letzte »    

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz