Entwickler-Ecke

Sonstiges (Delphi) - Problem mit Array / Record


drstar - Mo 25.09.17 14:17
Titel: Problem mit Array / Record
So, nach langer Zeit bin ich dann auch mal wieder mit einem Problem dabei. Ich schreibe derzeit ein Programm zur Dokumentenverwaltung, und habe dazu eine Klasse entwickelt, die eine Art Datenbank beinhaltet. In dieser Klasse sind auch eine Reihe von Records, zum Teil als Arrays, definiert. Normalerweise haben solche Arrays und Records ja eine eigene Speicherverwaltung, durch die Einbindung in die Klasse scheine ich diese Speicherverwaltung unabsichtlich auszuhebeln. Ganz konkret geht es mir um folgendes:

Unit Database

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
type TDatenbank               = class
  public
  type TKlient         = record
         Status        : integer;
         Index         : integer;
         Vorname       : string;
         Nachname      : string;
         Strasse       : string;
         Ort           : string;
       end;
       PKlient         = ^TKlient;
       TKlientliste    = record
         Count         : integer;
         Liste         : array of TKlient;
       end;
       PKlientliste    = ^TKlientliste;
       constructor create(Path: String);
       function  LastOperation: integer;
       {weitere Deklarationen, die hier keine Rolle spielen}
       private
       procedure LoadDB;
       procedure StoreValue(Klient: PKlient); overload;
       {weitere Deklarationen, die hier keine Rolle spielen}
       var  iKlientenliste      : TKlientliste;
            pKlientenliste      : PKlientliste;
            Error               : integer;
            Vorlagenpfad        : string;
            {weitere Deklarationen, die hier keine Rolle spielen}
end;


constructor TDatenbank.create(Path: String);
begin
  Vorlagenpfad := Path;
  LoadDB;
end;

procedure TDatenbank.LoadDB;
  new(PKlientenliste);
  PKlientenliste := @iKlientenliste;
  iKlientenliste.Count := 0//funktioniert offenbar, jedenfalls kein Fehler!
  {weiterer Code, der hier nicht relevant ist}
end;

procedure TDatenbank.NewKlient(Klient: PKlient);
begin
  StoreValue(Klient);
end;

procedure TDatenbank.StoreValue(Klient: PKlient);
begin
  iKlientenliste.Liste[iKlientenliste.Count+1] := Klient^;
  inc(iKlientenliste.Count);
end;


Unit NewClient

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TForm2.Button1Click(Sender: TObject);
var Klient  : TDatenbank.TKlient;
    AKlient : TDatenbank.PKlient;

begin

  With Klient do
  begin
    Status   := 1;
    Index    := 1;
    Vorname  := Edit1.Text;
    Nachname := Edit2.Text;
    Strasse  := Edit3.Text;
    Ort      := Edit4.Text;
  end;
  New(AKlient);
  AKlient := @Klient;
  Datenbank.NewKlient(AKlient); // Hier knallt es!
  Dispose(AKlient);
end;


In der Main habe ich die Klasse selbstverständlich instanziiert mit


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
var  Datenbank: TDatenbank;
     Error    : integer;

begin  
  Dtenbank := TDatenbank.create('');
  Error := Datenbank.LastOperation;
  if Error <> 0
  then
    case error of
      1: showmessage('Datenbank ist fehlerhaft, bitte Datenbanküberprüfung durchführen!');
      2: showmessage('Es existiert keine Datenbank, diese muß neu angelegt werden!');
    end;
end;
end;


Mein Problem ist der Zugriff auf Strukturen innerhalb der Datenbank (iKlientenliste) z. B. Dort wirft das Programm entweder Zugriffsverletzungen oder, wenn ich mit ReallocMem arbeite, Fehler bei der Bereichsprüfung. Wie kann ich diese Strukturen richtig initialisieren und mit Daten füttern? Die Zeiger sind ja (hoffentlich) richtig geetzt, ich vermute das Problem bei der Speicherverwaltung und wäre für Hilfe sehr dankbar.


Delete - Mo 25.09.17 15:21

- Nachträglich durch die Entwickler-Ecke gelöscht -


drstar - Mo 25.09.17 15:27

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Guten Tag drstar,

ich tippe mal, es liegt an dem dynamischen Array:


Delphi-Quelltext
1:
2:
3:
4:
TKlientliste = record
  Count: integer;
  Liste: array of TKlient;  // hier 
end;

Die Problemzeile:


Delphi-Quelltext
1:
Datenbank.NewKlient(AKlient); // Hier knallt es!                    

ruft StoreValue auf:


Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TDatenbank.StoreValue(Klient: PKlient);
begin
  iKlientenliste.Liste[iKlientenliste.Count+1] := Klient^;  // Array-Länge noch unbekannt ?!
  inc(iKlientenliste.Count);
end;

Dabei wird nirgends eine Länge für das dynamische Array mit SetLength() festgelegt.


Richtig, die gekapselte Routine StoreValue wird aufgerufen (soll auch so sein), da die Bearbeitung der Datenbank ausschließlich innerhalb der Datenbank geschehen soll, und somit lediglich die gefilterten Datensätze ausgespuckt werden sollen. Da spielt auch eine umfangreiche Fehlerbehandlung der Datenbank eine Rolle, die ich natürlich intern haben will.

Ich müßte also das Array anfangs mit SetLength(0) auf 0 festlegen, um es dann immer, wenn ich einen Wert hinzufüge, um eins zu erhöhen?

Edit:

Hab es jetzt wie folgt bearbeitet:

Unit Database

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
type TDatenbank               = class
  public
  type TKlient         = record
         Status        : integer;
         Index         : integer;
         Vorname       : string;
         Nachname      : string;
         Strasse       : string;
         Ort           : string;
       end;
       PKlient         = ^TKlient;
       TKlientliste    = record
         Count         : integer;
         Liste         : array of TKlient;
       end;
       PKlientliste    = ^TKlientliste;
       constructor create(Path: String);
       function  LastOperation: integer;
       {weitere Deklarationen, die hier keine Rolle spielen}
       private
       procedure LoadDB;
       procedure StoreValue(Klient: PKlient); overload;
       {weitere Deklarationen, die hier keine Rolle spielen}
       var  iKlientenliste      : TKlientliste;
            pKlientenliste      : PKlientliste;
            Error               : integer;
            Vorlagenpfad        : string;
            {weitere Deklarationen, die hier keine Rolle spielen}
end;


constructor TDatenbank.create(Path: String);
begin
  Vorlagenpfad := Path;
  LoadDB;
end;

procedure TDatenbank.LoadDB;
  new(PKlientenliste);
  PKlientenliste := @iKlientenliste;
  iKlientenliste.Count := 0//funktioniert offenbar, jedenfalls kein Fehler!
  setLength(iKlientenliste.Liste, 0);  //neu hinzugefügt
  {weiterer Code, der hier nicht relevant ist}
end;

procedure TDatenbank.NewKlient(Klient: PKlient);
begin
  StoreValue(Klient);
end;

procedure TDatenbank.StoreValue(Klient: PKlient);
begin
  setLength(iKlientenliste.Liste, iKlientenliste.Count+1);  //neu hinzugefügt
  iKlientenliste.Liste[iKlientenliste.Count+1] := Klient^;
  inc(iKlientenliste.Count);
end;


Unit NewClient

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TForm2.Button1Click(Sender: TObject);
var Klient  : TDatenbank.TKlient;
    AKlient : TDatenbank.PKlient;

begin

  With Klient do
  begin
    Status   := 1;
    Index    := 1;
    Vorname  := Edit1.Text;
    Nachname := Edit2.Text;
    Strasse  := Edit3.Text;
    Ort      := Edit4.Text;
  end;
  New(AKlient);
  AKlient := @Klient;
  Datenbank.NewKlient(AKlient); // Hier knallt es!
  Dispose(AKlient);
end;


In der Main habe ich die Klasse selbstverständlich instanziiert mit


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
var  Datenbank: TDatenbank;
     Error    : integer;

begin  
  Dtenbank := TDatenbank.create('');
  Error := Datenbank.LastOperation;
  if Error <> 0
  then
    case error of
      1: showmessage('Datenbank ist fehlerhaft, bitte Datenbanküberprüfung durchführen!');
      2: showmessage('Es existiert keine Datenbank, diese muß neu angelegt werden!');
    end;
end;
end;


Das Problem bleibt bestehen.


Delete - Mo 25.09.17 15:41

- Nachträglich durch die Entwickler-Ecke gelöscht -


drstar - Mo 25.09.17 15:49

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Deine Klientenliste hat doch ein Count-Attribut. Du brauchst einzig danach die Array-Länge auszurichten. Count sollte von Anfang an mit 0 initialisiert werden und mit jeden neuen Klienten erhöht oder beim Löschen, vermindert werden. Im Anschluss setzt du erneut die Array-Länge auf Count und das wars.


Ist doch bereits implementiert - der Fehler bleibt aber (Fehler bei Bereichsprüfung), siehe meine Antwort zuvor, dort habe ich genau das eingefügt.


Delete - Mo 25.09.17 15:57

- Nachträglich durch die Entwickler-Ecke gelöscht -


OlafSt - Mo 25.09.17 16:13

Eigentlich ist das ganze Design der Klasse... sagen wir gewöhnungsbedürftig.

Heutzutage ist TKlient ebenfalls eine Klasse. Und das Array, das so viele Probleme macht, ist entweder eine TObjectList (bis D2009) oder eine TObjectList<TKlient>.

Sobald man diese ObjectList hat, entfällt das komplette Geraffel mit Speicher holen und freigeben und verwalten. Macht alles die ObjectList.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
  type TKlient         = class(TObject)
  public
         Status        : integer;
         Index         : integer;
         Vorname       : string;
         Nachname      : string;
         Strasse       : string;
         Ort           : string;
       end;

type TDatenbank               = class
  public
       Klientliste: TObjectList<TKlient>;

       constructor create(Path: String);
       function  LastOperation: integer;
       {weitere Deklarationen, die hier keine Rolle spielen}
       private
       procedure LoadDB;
       procedure StoreValue(Klient: TKlient); overload;
       {weitere Deklarationen, die hier keine Rolle spielen}
            Error               : integer;
            Vorlagenpfad        : string;
            {weitere Deklarationen, die hier keine Rolle spielen}
end;

constructor TDatenbank.Create(Path: string)
begin
   KlientListe:=TObjectList<TKlient>.Create(true);
   ...
end;


drstar - Mo 25.09.17 16:37

user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Eigentlich ist das ganze Design der Klasse... sagen wir gewöhnungsbedürftig.

Heutzutage ist TKlient ebenfalls eine Klasse. Und das Array, das so viele Probleme macht, ist entweder eine TObjectList (bis D2009) oder eine TObjectList<TKlient>.

Sobald man diese ObjectList hat, entfällt das komplette Geraffel mit Speicher holen und freigeben und verwalten. Macht alles die ObjectList.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
  type TKlient         = class(TObject)
  public
         Status        : integer;
         Index         : integer;
         Vorname       : string;
         Nachname      : string;
         Strasse       : string;
         Ort           : string;
       end;

type TDatenbank               = class
  public
       Klientliste: TObjectList<TKlient>;

       constructor create(Path: String);
       function  LastOperation: integer;
       {weitere Deklarationen, die hier keine Rolle spielen}
       private
       procedure LoadDB;
       procedure StoreValue(Klient: TKlient); overload;
       {weitere Deklarationen, die hier keine Rolle spielen}
            Error               : integer;
            Vorlagenpfad        : string;
            {weitere Deklarationen, die hier keine Rolle spielen}
end;

constructor TDatenbank.Create(Path: string)
begin
   KlientListe:=TObjectList<TKlient>.Create(true);
   ...
end;


Soweit ich das verstanden habe, ist dann jeder Datensatz ein eigenes Objekt - ich sehe nicht, inwiefern mir das alles einfacher machen soll. Nicht nur, daß ich alles umschreiben müßte, ich müßte mir dann erstmal Gedanken machen, wie ich es anstelle, meine Datensätze als Objekte zu behandeln.
Dennoch verstehe ich nicht, weshalb es zu diesem Fehler kommt. In meiner ersten Antwort (dritter Beitrag dieses Threads) habe ich bei der Initialisierung nicht nur Count auf 0 gesetzt (hatte ich vorher schon), sondern eben auch SetLength auf 0 gesetzt. StoreValue hat ihn dann um 1 erhöht (SetLength := Count+1). Funktioniert auch nicht - irgendwo muß ich also einen Denkfehler haben. Bevor ich jetzt anfange, alles auf TObjectList umzuschreiben, würde ich lieber gern den Fehler ausmerzen - sollte das dann halt nicht gelingen, werde ich mich intensiver mit TObjectList beschäftigen.


Delete - Mo 25.09.17 16:51

- Nachträglich durch die Entwickler-Ecke gelöscht -


drstar - Mo 25.09.17 16:57

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Dein Denkfehler war/ist:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
// Dein Edit
procedure TDatenbank.StoreValue(Klient: PKlient);
begin                                                       // Angenommen: Count = 0
  setLength(iKlientenliste.Liste, iKlientenliste.Count+1);  // ArrayLänge = 1 --> .Liste[0] kann initialisiert werden
  iKlientenliste.Liste[iKlientenliste.Count+1] := Klient^;  // .Liste[1] wird Klient zugewiesen, gibts aber nicht!
  inc(iKlientenliste.Count);                                // Count = 0 + 1 = 1
end;


hab gerade den Code testweise wie folgt geändert:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
 
procedure TDatenbank.StoreValue(Klient: PKlient);
begin                                                       // Count ist zu Beginn 0, wird bei Initialisierung von Datenbank auf 0 gesetzt
  setLength(iKlientenliste.Liste, iKlientenliste.Count+1);  // hab ArrayLänge auf 1 gesetzt
  iKlientenliste.Liste[iKlientenliste.Count] := Klient^;    // schreibe in Array.Liste[Count], anfangs also 0
  inc(iKlientenliste.Count);                                // Count auf 1 gesetzt (bzw. später inkrementiert)
end;


Den Code mag das Programm gar nicht - ungültige Zeigeroperation.


Delete - Mo 25.09.17 17:07

- Nachträglich durch die Entwickler-Ecke gelöscht -


drstar - Mo 25.09.17 17:17

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
1.) Wo wird:
iKlientenliste.Count := 0; zugwiesen? Würde ich z.B. im Konstruktor der Klasse erwarten.
2.) Hast du mein Beispiel hier [https://www.entwickler-ecke.de/viewtopic.php?p=708699#708699] übersehen?


Die Zuweisung erfolgt in der Routine TDatenbank.DBLoad, dort zu finden, ich hatte dort auch geschrieben, daß ich problemlos Count auf 0 setzen konnte ohne Fehler.

Die Erhöhung von Count sowie das SetLength von iKlientenliste erfolgt unter StoreValue, da dort auch die Zuweisung und Speicherung des Datensatzes erfolgt. Übersehen habe ich es demnach nicht.


drstar - Do 28.09.17 15:52

So, nun habe ich versucht, es mit TObjectList zu probieren und falle gleich mal auf's Maul.

[dcc32 Fehler] Database.pas(62): E2003 Undeklarierter Bezeichner: 'TObjectList<>'

Fehlt mir eine Unit in der Uses-Klausel oder was muß ich sonst anpassen? Habe Delphi 10.1, falls das relevant ist.

Edit:

Hat sich erledigt, System.Generics.Collections ist die benötigte Unit.


drstar - Do 28.09.17 18:33

So, ich hab jetzt auf TObjectList umgestellt. Funktioniert eigentlich auch alles, nur ein Problem habe ich: TObjectList.count fängt bei 0 an - wie ermittle ich, ob überhaupt schon Objekte alias Datensätze in der Liste drin sind? Count auf 0 prüfen scheidet ja aus, denn 0 ist bereits ein Objekt. Wie stelle ich fest, ob schon Objekte gespeichert sind, bevor ich versuche, auf Objekte in der Liste zuzugreifen, die womöglich nicht existieren?


mandras - Do 28.09.17 20:37

Hallo drstar,

Count kannst/mußt du nehmen.

Wenn die Liste leer ist ist Count 0.

Wenn 3 Einträge in der Liste sind so sind dies die Einträge 0,1 und 2


gerd8888 - Do 28.09.17 20:50

Hallo drstar,

Zitat:

Count auf 0 prüfen scheidet ja aus, denn 0 ist bereits ein Objekt. Wie stelle ich fest, ob schon Objekte gespeichert sind

[/quote]

ich würde das count mit NIL prüfen. Das ist dann der Wert bei Count -1 !


mandras - Do 28.09.17 21:10

> ich würde das count mit NIL prüfen.

TObjectList.count ist ein Integer - also muß auf 0 geprüft werden


gerd8888 - Do 28.09.17 21:18

Hallo mandras,

soweit ich weiss, ist es dann bei dynamischen array (count) -1 entspricht NIL.
Ich bin mir jetzt auch nicht mehr ganz sicher. Normalerweise nimmt man NIL her. Das geht 100 prozentig.


mandras - Do 28.09.17 22:32

drstar schrieb daß nun ein Objekt vom Typ TObjectList verwendet wird.

Hier wird per Count:integer festgehalten wie viele Elemente daran sind


OlafSt - Fr 29.09.17 01:25

gerd888 erzählt Unsinn. Wenn die TObjectList<> nicht initialisiert ist, ist die ganze ObjectList Nil. Ein Zugriff auf das Count-Property gibt dann auch prompt eine Exception.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
var
  TOL: TObjectList<TSomething>;
begin
  if Assigned(TOL) then  //TOL = Nil, wenn noch nicht alloziert
  begin
    if TOL.Count > 0 then //Wenn Liste leer ist Count=0
    begin
      ...
    end;
  end;
end;


doublecross - Fr 29.09.17 10:39

Hi,
user profile icongerd8888 hat folgendes geschrieben Zum zitierten Posting springen:
Ich bin mir jetzt auch nicht mehr ganz sicher. Normalerweise nimmt man NIL her. Das geht 100 prozentig.

"man" ist immer relativ. Da ich auch ganz gerne mal die Sprace wechsel und soetwan hüben meist anderes läuft als drüben prüfe ich z.B. gerne auf < 1, dass klappt eigentlich immer bei "Anzahl Elemente" Prüfungen. Das ist ja das schöne beim Programmieren, solange es funktioniert ist es richtig - alles andere ist nur Religion.


drstar - Fr 29.09.17 11:32

user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
gerd888 erzählt Unsinn. Wenn die TObjectList<> nicht initialisiert ist, ist die ganze ObjectList Nil. Ein Zugriff auf das Count-Property gibt dann auch prompt eine Exception.



Die Liste wird natürlich schon beim create initialisiert, daher kann an dieser Stelle keine Exception auftreten. Bevor ich aber blind auf vermeintliche Elemente der Liste zugreifen will, will ich natürlich feststellen, ob sich überhaupt schon Einträge in der Liste befinden. Die Prüfung auf 0 scheint nicht zu funktionieren, denn auch eine leere Liste ergibt 0, und zumindest bei meinem Test auch mit genau 1 Element. Ansonsten mache ich an anderer Stelle irgendetwas falsch. Die einzige Option, die mir bliebe, wäre ein "blindes" Element beim create einzufügen und das fortan zu ignorieren - dann ergibt die Prüfung beim Count natürlich, sobald ich ein "echtes" Element der Liste hinzufüge, Count > 0. Wäre zwar nicht schön, aber im Zweifelsfalle könnte ich damit leben. Gibt's halt ein nicht benutztes Element in der Liste...


OlafSt - Fr 29.09.17 12:56

Das eine Liste mit einem eingefügten Element trotzdem ein Count=0 zurückgeben soll, halte ich für ausgeschlossen. Dann wären die einschlägigen Foren voll mit Threads zu diesem Thema.

Ich tippe dann eher darauf, das das Element gar nicht eingefügt wird (weil es zu dem Zeitpunkt schon wieder/noch nil ist, oder da ist irgendwo ein Liste.Clear; versehentlich reingerutscht, oder oder oder...


drstar - Fr 29.09.17 13:21

user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Das eine Liste mit einem eingefügten Element trotzdem ein Count=0 zurückgeben soll, halte ich für ausgeschlossen. Dann wären die einschlägigen Foren voll mit Threads zu diesem Thema.

Ich tippe dann eher darauf, das das Element gar nicht eingefügt wird (weil es zu dem Zeitpunkt schon wieder/noch nil ist, oder da ist irgendwo ein Liste.Clear; versehentlich reingerutscht, oder oder oder...


In der Tat liegt der Fehler woanders - beim Überwachen ergab ein Element im .Count auch 1. Also Prüfung auf > 0 ist korrekt. Ich bin noch nicht dahinter gestiegen, was bei mir das Problem verursacht, aber ich vermute folgendes:

main.pas


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure main
begin
  Datenbank.NewKlient(Klient);   // die Übergabe klappt, in der TObjectList der Datenbank-Klasse landet das Objekt
end;

procedure Liste_aktualisieren;
var lListe: TObjectList<TKlient>;
begin
  lListe := TObjectList<TKlient>.create(true);
  Datenbank.GetKlientenliste(lListe);  //hier bekomme ich keine Liste zurück, wie es ausschaut, bzw. ist die Liste nil
end;


database.pas


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
type Datenbank = class(TObjeckt)
  public
    procedure NewKlient(Klient: TKlient)
    procedure GetKlientenliste(Liste: TObjectList<TKlient>);
  private
    procedure StoreValue(Klient: TKlient); overload
    iKlientliste : TObjectList<TKlient>;
  end;

procedure Datenbank.NewKlient; overload;
begin
//hier klappt alles, die interne Liste wird befüllt, testweise gleich zu Beginn mit einem Element
end;

procedure Datenbank.GetKlientenliste(Liste: TObjectList<TKlient>);
var i: integer;
begin
  Liste := TObjectList<TKlient>.create(true);
  if iKlientliste.count > 0 //da ein Element vorhanden, geht es auch in die Schleife
  then
  begin
    for i := 1 to iKlientliste.count do
      Liste[i] := iKlientliste[i];
  end
  else
    Liste := Nil;
  end;
  //Liste.destroy darf hier nicht aufgerufen werden, denke ich mal?
end;


Für mich scheint es so, als gäbe es bei der Übergabe der Liste aus der Get-Methode der Datenbank-Klasse zu einem Problem. Wie bekomme ich die Liste übergeben?


jasocul - Fr 29.09.17 13:40

Count gibt die Anzahl der Elemente an. Das ist wohl klar.
Deine Schleife geht von 1 bis Count.

Immer daran denken, dass der Listen-Index immer bei 0 anfängt!

Deine Schleife muss also so aussehen:

Delphi-Quelltext
1:
2:
   for i := 0 to iKlientliste.count-1 do
      Liste[i] := iKlientliste[i];


Abgesehen davon, dass du dein Listen-Objekt zweimal erzeugst. Einmal vor GetKlientenliste und dann noch innerhalb dieser Methode.

Außerdem müsste es innerhalb der Zuweisung in deiner Schleife auch knallen. Deine Zielliste hat noch keinen Inhalt. Der Zugriff mit Liste[i] muss eine Exception verursachen, da ein Listen-Element mit diesem Index noch gar nicht existiert.


drstar - Fr 29.09.17 13:51

user profile iconjasocul hat folgendes geschrieben Zum zitierten Posting springen:
Count gibt die Anzahl der Elemente an. Das ist wohl klar.
Deine Schleife geht von 1 bis Count.

Immer daran denken, dass der Listen-Index immer bei 0 anfängt!

Deine Schleife muss also so aussehen:

Delphi-Quelltext
1:
2:
   for i := 0 to iKlientliste.count-1 do
      Liste[i] := iKlientliste[i];


Abgesehen davon, dass du dein Listen-Objekt zweimal erzeugst. Einmal vor GetKlientenliste und dann noch innerhalb dieser Methode.

Außerdem müsste es innerhalb der Zuweisung in deiner Schleife auch knallen. Deine Zielliste hat noch keinen Inhalt. Der Zugriff mit Liste[i] muss eine Exception verursachen, da ein Listen-Element mit diesem Index noch gar nicht existiert.


Den Schleifen-Index habe ich jetzt auf 0.. count-1 umgestellt, aber das führt leider auch nicht zum Erfolg. Die Schleife läuft tadellos durch, und laut Überwachung wird die Liste in der Get-Methode der Datenbank auch befüllt. Die Liste kommt aber in der Main nie an, sondern ist immer Nil. Das mit dem doppelt erzeugen habe ich mir auch schon gedacht, hab mal testweise das .create in der Datenbank rausgenommen, ändert aber leider nichts, außer das es nicht zu einem anderen Fehler kommt, was für mich den Schluß zuläßt, das das .create in der Get-Methode nichts zu suchen hat. Knackpunkt scheint aber die Übergabe/Rückgabe der Liste zu sein.


jasocul - Fr 29.09.17 14:01

Zitat:
Abgesehen davon, dass du dein Listen-Objekt zweimal erzeugst. Einmal vor GetKlientenliste und dann noch innerhalb dieser Methode.

Hmm. War ich mal wieder zu subtil. :?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure Datenbank.GetKlientenliste(var Liste: TObjectList<TKlient>); // Anpassung Jasocul
var i: integer;
begin
  if not Assigned(Liste) then // Anpassung Jasocul
    Liste := TObjectList<TKlient>.create(true);
  if iKlientliste.count > 0 //da ein Element vorhanden, geht es auch in die Schleife
  then
  begin
    for i := 0 to iKlientliste.count-1 do // Anpassung Jasocul
      Liste[i] := iKlientliste[i];
  end
  else
    Liste.Free; // Anpassung Jasocul Alternativ für beide Zeilen: FreeAndNil(Liste)
    Liste := Nil;
  end;
  //Liste.destroy darf hier nicht aufgerufen werden, denke ich mal?
end;


jasocul - Fr 29.09.17 14:07

Noch ein paar Korrekturen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure Datenbank.GetKlientenliste(var Liste: TObjectList<TKlient>); // Anpassung Jasocul
var i: integer;
begin
  if not Assigned(Liste) then // Anpassung Jasocul
    Liste := TObjectList<TKlient>.create(true);
  if iKlientliste.count > 0 //da ein Element vorhanden, geht es auch in die Schleife
  then
  begin
    for i := 0 to iKlientliste.count-1 do // Anpassung Jasocul
      Liste[i] := iKlientliste[i]; // Hier ist noch was zu tun
  end
  else
  begin
    Liste.Free; // Anpassung Jasocul Alternativ für beide Zeilen: FreeAndNil(Liste)
    Liste := Nil;
  end;
end;
  //Liste.destroy darf hier nicht aufgerufen werden, denke ich mal?
end;


drstar - Fr 29.09.17 14:40

user profile iconjasocul hat folgendes geschrieben Zum zitierten Posting springen:
Noch ein paar Korrekturen:

Delphi-Quelltext
1:
2:
3:
4:
[...]
    for i := 0 to iKlientliste.count-1 do // Anpassung Jasocul
      Liste[i] := iKlientliste[i]; // Hier ist noch was zu tun
[...];


Hier lag der Fehler, richtig ist


Delphi-Quelltext
1:
2:
3:
     for i := 0 to iKlientliste.Count-1 do
    begin
      Klientliste.Add(iKlientliste.Items[i]);


jetzt funzt es, vielen Dank. damit markiere ich dieses Problem als gelöst, vielen vielen Dank!