Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi generische ObjectList: Löschen eines Elementes (https://www.delphipraxis.net/183657-generische-objectlist-loeschen-eines-elementes.html)

Helmi 26. Jan 2015 18:31

generische ObjectList: Löschen eines Elementes
 
Hallo,

ich hab mal eine Verständnisfrage.

Ich habe eine generische ObjectList:
Delphi-Quellcode:
ObjectList_sichtbare_Masken := TObjectList<TMaskeFeld>.Create(false);
.

In dieser Procedure wird aus der Liste gelöscht:
Delphi-Quellcode:
procedure Masken_ausblenden(akt_MaskID: String);
  var
    i       : Integer;
    MaskeFeld: TMaskeFeld;

  begin
    for MaskeFeld in ObjectList_sichtbare_Masken do
      If MaskeFeld.NameID <> akt_MaskID then
        begin
          MaskeFeld.Visible := false;

          i := ObjectList_sichtbare_Masken.IndexOf(MaskeFeld);
          ObjectList_sichtbare_Masken.Delete(i);
        end;
  end;
Früher hatte ich keine generische ObjectList, sonder eine normale ObjectList
Delphi-Quellcode:
ObjectList_sichtbare_Masken := TObjectList.Create(false);
und hab so gelöscht:
Delphi-Quellcode:
procedure Masken_ausblenden(akt_MaskID: String);
  var
    i       : Integer;
    MaskeFeld: TMaskeFeld;

  begin
    for Pred(ObjectList_sichtbare_Masken.Count) downto 0 do
      begin
        MaskeFeld := TMaskeFeld(ObjectList_sichtbare_Masken.Items[i]);

        If MaskeFeld.NameID <> akt_MaskID then
          begin
            MaskeFeld.Visible := false;
 
            ObjectList_sichtbare_Masken.Delete(i);
          end;
      end;
  end;
(grob aus dem Gedächtnis getippt - hab leider den originalen Code nicht mehr)

"Früher", also bei der normalen ObjectList musste man ja die for-Schleife rückwärts laufen lassen,
wenn man ein Element löschen will, da sich ja durch das Löschen die Anzahl ändert.

Aber warum brauch ich das bei der generischen ObjectList nicht mehr?
Ich kann es mir nur so erklären, dass ich in der Schleife direkt ein Object bekomme und nicht, wie früher, einen Index.

Dejan Vu 26. Jan 2015 18:42

AW: generische ObjectList: Löschen eines Elementes
 
Also bei C# darfst Du die Liste nicht verändern, während Du durch sie durch iterierst. Hier ist das vermutlich anders.
Aber sag mal, findest Du das nicht ein wenig umständlich, durch die Liste mit einem Iterator zu laufen, den Index zu ermitteln und dann zu löschen. Das ist doch viel langsamer als eine Schleife.
Delphi-Quellcode:
For i:= myList.Count-1 downto 0 do
  if EntryIsNotValid(myList[i]) then
    myList.Delete(i);
Nur weil es das 'for in' gibt, muss man es doch nicht immer verwenden...

Helmi 26. Jan 2015 18:47

AW: generische ObjectList: Löschen eines Elementes
 
Zitat:

Zitat von Dejan Vu (Beitrag 1287851)
Nur weil es das 'for in' gibt, muss man es doch nicht immer verwenden...

mag jetzt vielleicht etwas komisch sein ja, aber um das geht's erstmal nicht

himitsu 26. Jan 2015 18:47

AW: generische ObjectList: Löschen eines Elementes
 
Du musst dir nur einen Enumerator basteln, der halt rückwärts durch die Liste läuft. (
Delphi-Quellcode:
for MaskeFeld in ObjectList_sichtbare_Masken.Reverse do
)
(den gibt es nur standardmäßig nicht im fertig in die Delphi-Listen eingebaut :stupid: , so wie es auch keine Filter gibt :wall: )

oder du arbeitest mir einem Zwischenspeicher, so ala
Delphi-Quellcode:
for MaskeFeld in ObjectList_sichtbare_Masken.ToArray do
.


Und dann gibt es auch noch generische Listen von anderen Leuten, die sowas und mehr drin haben.

Helmi 26. Jan 2015 18:50

AW: generische ObjectList: Löschen eines Elementes
 
die Procedure mit der generischen Liste funktioniert ja sehr gut
Nur frage ich mich, warum das so problemlos geht

Dejan Vu 26. Jan 2015 18:53

AW: generische ObjectList: Löschen eines Elementes
 
Zitat:

Zitat von Helmi (Beitrag 1287852)
mag jetzt vielleicht etwas komisch sein ja, aber um das geht's erstmal nicht

Echt nicht? Na gut.

Warum muss man das nicht machen? Weil der Iterator immun dagegen ist, das Du ihm das aktuelle Objekt unter dem Hintern wegziehst, vielleicht. Und warum? Dazu musst Du in die Implementierung schauen, vermutlich prüft er jedes mal, ob noch Elemente da sind.

Oder das funktioniert erst gar nicht, weil der Delphi-Iterator gar nicht merkt, das die Liste verändert wird.

Was passiert denn, wenn Du eine Liste mit zwei Element hast und dann deine Schleife startest? Sind beide Elemente weg?

Helmi 26. Jan 2015 18:59

AW: generische ObjectList: Löschen eines Elementes
 
ich merkt halt dahingegen dass es funktioniert, dass die MaskeFelder (sind eigentlich Panels) nicht mehr vorhanden sind

Der schöne Günther 26. Jan 2015 19:23

AW: generische ObjectList: Löschen eines Elementes
 
Zitat:

Zitat von Helmi (Beitrag 1287858)
ich merkt halt dahingegen dass es funktioniert

Es funktioniert nur, weil du in der Schleife neu bestimmst, an welcher Position das Element steht das du löschen willst. Die Alternative bei einer reinen Pointer-basierten Liste (fester Index und rückwärts) funktioniert natürlich noch genauso.

Dejan Vu 26. Jan 2015 20:14

AW: generische ObjectList: Löschen eines Elementes
 
Zitat:

Zitat von Helmi (Beitrag 1287858)
ich merkt halt...

Merken <> Wissen. Probiere es lieber aus ;-)

Helmi 26. Jan 2015 20:20

AW: generische ObjectList: Löschen eines Elementes
 
huch - da ist ein Problem! :-)

Mist - geändert - aber nie komplett getestet

himitsu 26. Jan 2015 20:36

AW: generische ObjectList: Löschen eines Elementes
 
Zitat:

Delphi-Quellcode:
procedure Masken_ausblenden(akt_MaskID: String);
  var
    i : Integer;
    MaskeFeld: TMaskeFeld;
  begin
    for MaskeFeld in ObjectList_sichtbare_Masken do
      If MaskeFeld.NameID <> akt_MaskID then
        begin
          MaskeFeld.Visible := false;

          i := ObjectList_sichtbare_Masken.IndexOf(MaskeFeld);
          ObjectList_sichtbare_Masken.Delete(i);
        end;
  end;

Delphi-Quellcode:
procedure Masken_ausblenden(akt_MaskID: String);
  var
    MaskeFeld: TMaskeFeld;
  begin
    for MaskeFeld in ObjectList_sichtbare_Masken.Reverse do
      If MaskeFeld.NameID <> akt_MaskID then
        begin
          MaskeFeld.Visible := False;
          ObjectList_sichtbare_Masken.Remove(MaskeFeld);
        end;
  end;
Aber auch bei generischen Listen kann man für sowas immernoch eine For-Do-Schleife benutzen.

Oder vielleicht auch so?
Delphi-Quellcode:
procedure Masken_ausblenden(akt_MaskID: String);
  var
    MaskeFeld, MaskeFeld2: TMaskeFeld;
  begin
    MaskeFeld := ObjectList_sichtbare_Masken.Find(MaskID); // sowas ist das Erste, was man sich baut - womöglich mit passendem Comperator
    for MaskeFeld2 in ObjectList_sichtbare_Masken do
      MaskeFeld2.Visible := MaskeFeld2 = MaskeFeld;
    ObjectList_sichtbare_Masken.Clear;
    ObjectList_sichtbare_Masken.Add(MaskeFeld);
  end;

Helmi 26. Jan 2015 20:41

AW: generische ObjectList: Löschen eines Elementes
 
Zitat:

Zitat von himitsu (Beitrag 1287869)
Oder vielleicht auch so?
Delphi-Quellcode:
procedure Masken_ausblenden(akt_MaskID: String);
  var
    MaskeFeld, MaskeFeld2: TMaskeFeld;
  begin
    MaskeFeld := ObjectList_sichtbare_Masken.Find(MaskID); // sowas ist das Erste, was man sich baut - womöglich mit passendem Comperator
    for MaskeFeld2 in ObjectList_sichtbare_Masken do
      MaskeFeld2.Visible := MaskeFeld2 = MaskeFeld;
    ObjectList_sichtbare_Masken.Clear;
    ObjectList_sichtbare_Masken.Add(MaskeFeld);
  end;

Ist gut gemeint (und ein toller Vorschlag) - nur das Visiblen passiert ggf. wo anders
daher ist es nicht so einfach

ich bin jetzt einfach wieder auf die alte for-Schleife umgestiegen, aber halt in Verbindung mit Generics

Delphi-Quellcode:
  procedure Masken_ausblenden(akt_MaskID: String);
  var
    i       : Integer;
    MaskeFeld: TMaskeFeld;

  begin
    for i := Pred(ObjectList_sichtbare_Masken.Count) downto 0 do
      begin
        MaskeFeld := ObjectList_sichtbare_Masken.Items[i];

        If MaskeFeld.NameID <> akt_MaskID then
          begin
            MaskeFeld.Visible := false;

            ObjectList_sichtbare_Masken.Delete(i);
          end;
      end;
  end;

Dejan Vu 26. Jan 2015 21:43

AW: generische ObjectList: Löschen eines Elementes
 
Ich finde Iteratoren ja auch ganz elegant, aber wenn ich etwas mit der Liste anstellen muss (oder den Index brauche), dann nehe ich auch die gute alte for-Schleife. Einfach, klar, direkt und kein überflüssiger Firlefanz.

Mich wundert es, das Delphi erlaubt, während der Verwendung eines Iterators die Liste manipulieren zu dürfen. Das ist sehr unsauber.

Sir Rufo 26. Jan 2015 22:09

AW: generische ObjectList: Löschen eines Elementes
 
Und? Wenn der das nicht erlauben würde dann einfach so
Delphi-Quellcode:
for LItem in MyList.ToArray do
  MYList.Remove( LItem );
;)

Nicht, das jemand meint das wäre jetzt schneller als über den Index, aber es geht halt :stupid:

jaenicke 27. Jan 2015 06:53

AW: generische ObjectList: Löschen eines Elementes
 
Mit ToArray mache ich das auch meistens, ja. Der Geschwindigkeitsunterschied ist in den meisten Fällen vernachlässigbar und so ist es einfach schöner lesbar.

Dejan Vu 27. Jan 2015 06:58

AW: generische ObjectList: Löschen eines Elementes
 
Hmm.. Wenn "MyList" noch keinen Zugriff über einen Index hätte (wie bei einem Dataset z.B.), würde ich das auch so machen, aber so... Ist das irgendwie ein ziemlich überflüssiger Schritt.

Aber klar: Wieso einfach, wenns auch umständlich geht.

Der schöne Günther 27. Jan 2015 07:56

AW: generische ObjectList: Löschen eines Elementes
 
Zitat:

Zitat von Dejan Vu (Beitrag 1287875)
Mich wundert es, das Delphi erlaubt, während der Verwendung eines Iterators die Liste manipulieren zu dürfen. Das ist sehr unsauber.

Ich verstehe nicht- Tut es doch auch nicht? Nur weil einem keine Schutzverletzungen um die Ohren fliegen heißt das doch nicht, dass es erlaubt ist nur weil es grade mal funktioniert hat?

Dejan Vu 27. Jan 2015 10:27

AW: generische ObjectList: Löschen eines Elementes
 
Ein FailFast wäre schon was feines im 'Remove', wenn ein Iterator gerade aktiv ist. Scheint aber keine Sau zu stören. Insofern ist Delphi hier näher an C dran, als an C#. Blöd.

Der schöne Günther 27. Jan 2015 10:38

AW: generische ObjectList: Löschen eines Elementes
 
Die Borland Standard-Collections kann man sich sowieso abgewöhnen- Die Collections aus Spring4D wrappen (ich glaube allesamt) zwar die Standard-Collections, aber haben auch daran gedacht: Wenn der Iterator eins weiter wandert und sich die Collection geändert hat fliegt man raus. 8-)

(Nicht das Entfernen aus der Collection)-
Bekommt das noch deinen Segen?

Stevie 27. Jan 2015 11:15

AW: generische ObjectList: Löschen eines Elementes
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1287911)
Die Collections aus Spring4D wrappen (ich glaube allesamt) zwar die Standard-Collections, aber haben auch daran gedacht: Wenn der Iterator eins weiter wandert und sich die Collection geändert hat fliegt man raus. 8-)

Nachdem jeder im Team mindestens einmal solchen Code geschrieben hat, der dann still und leise falsche Daten zurück ließ, hab ich das seinerzeit eingebaut damit mans direkt mitbekommt :)

Was mir in Verbindung mit der Frage noch einfiel ist, dass ich mal in IList<T> ein RemoveAll einbauen sollte.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:10 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