Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Tutorials und Kurse (https://www.delphipraxis.net/36-tutorials-und-kurse/)
-   -   Verwalten von Objekten in einer Container-Klasse (https://www.delphipraxis.net/88218-verwalten-von-objekten-einer-container-klasse.html)

Luckie 12. Mär 2007 00:51


Verwalten von Objekten in einer Container-Klasse
 
Liste der Anhänge anzeigen (Anzahl: 2)
Verwalten von Objekten in einer Container-Klasse

Abstract
Wie man OOP konform Objekte in einer Container-Klasse verwaltet.

Problemstellung
Oftmals hat man das Problem, dass man mehrere gleichartige Elemente verwalten muss - zum Beispiel Adressen in einem Adressbuch oder Spieler eines Spieles. Meist wird dann ein Record genommen und diese Records werden dann in einem dynamische Array gespeichert. Dies ist zum einem etwas umständlich und mit einem gewissen Aufwand verbunden und zum dem ist es nicht OOP konform.

Lösungsmöglichkeit
Um dies Problem OOP konform zu lösen, arbeitet man mit zwei Klassen: Einer Container-Klasse und einer Klasse für die zu verwaltenden Objekte. Anstatt eines Records wird also eine Klasse benutzt und die Objekte dieser Klasse werden nicht in einem dynamischen Array abgelegt, sondern in einer Container-Klasse. Programmiert man in Delphi, werden einem schon verschiedene Klassen angeboten, die man als Container-Klasse verwenden kann: TList, TObjectList und TCollection.
  • TList ist eine einfache Liste, in der beliebige Pointer abgelegt werden können. Die Klasse bringt schon Methoden zum Hinzufügen, Löschen usw. mit.
  • TObjectList ist eine Erweiterung der TList-Klasse und besonders für Objekte geeignet, da sie auch den Speicherplatz der Objekte selber verwalten kann.
  • TCollection ist eine spezielle Klasse, um Objekte vom Typ TCollectionItem zu verwalten. (Näheres dazu in der Delphi-Hilfe.)
Die Container-Klasse
Die Container-Klasse kapselt eine Liste vom Typ TList. (Man könnte auch eine Liste vom Typ TObjectList nehmen, was in der Praxis wohl auch sinnvoller wäre, da man dann den Speicher nicht selber verwalten muss. Ich habe mich hier aber für die Klasse TList entschieden, um zu zeigen, wie man den Speicher selber verwalten müsste in diesem Fall.) Da man die Liste kapselt, kann man selber bestimmen, welche Methoden mit welchen Parametern sichtbar sein sollen. Somit kann man dann auch sicherstellen, dass nur Objekte einer bestimmten Klasse in der Liste aufgenommen werden können. Wir haben uns somit eine streng typisierte Liste geschaffen. Eine Container-Klasse könnte dann zum Beispiel so aussehen:
Delphi-Quellcode:
TContactList = class(TObject)
private
  FInnerList: TList;
  function GetItem(Index: Integer): TContact;
  procedure SetItem(Index: Integer; Contact: TContact);
  function GetCount: Integer;
public
  constructor Create;
  destructor Destroy; override;
  procedure Add(Contact: TContact);
  procedure Delete(Index: Integer);
  property Count: Integer read GetCount;
  property Items[Index: Integer]: TContact read GetItem write SetItem;
end;
Die Methoden der typisierte Liste
Gucken wir uns exemplarisch die Methode Add unserer List an:
Delphi-Quellcode:
procedure TContactList.Add(Contact: TContact);
begin
  FInnerList.Add(Contact);
end;
Unsere eigene Methode Add ruft also im Prinzip nur die Methode Add von unser inneren Liste FInnerList (die natürlich im Konstruktor erzeugt und im Destruktor wieder freigegeben werden muss) auf. Da man allerdings nur ein Objekt der Klasse TContact übergeben kann, kann man nur Objekte diesen Typs in die Liste aufnehmen.

Den Speicher verwalten
Wie schon gesagt muss man den Speicher selber verwalten, wenn man keine Liste vom Typ TObjectList nimmt. Da man in der Liste eine Instanz einer Klasse ablegt, die Speicher belegt, muss man diesen Speicher auch wieder freigeben, wenn man ein Objekt aus der Liste entfernt oder, wenn man die ganze Liste wieder freigibt. Löscht man einen Eintrag der Liste, sähe dies dann so aus:
Delphi-Quellcode:
procedure TContactList.Delete(Index: Integer);
begin
  // destroy object
  TObject(FInnerList.Items[Index]).Free;
  // delete object from the list
  FinnerList.Delete(Index);
end;
Erst wird das Objekt in der Liste freigegeben und dann aus der selbigen gelöscht. Ebenso verfährt man beim Freigeben der Container-Klasse:
Delphi-Quellcode:
destructor TContactList.Destroy;
var
  i                : Integer;
begin
  if FInnerList.Count > 0 then
  begin
    for i := FInnerList.Count - 1 downto 0 do
    begin
      TObject(FInnerList.Items[i]).Free;
    end;
  end;
  FInnerList.Free;
  inherited;
end;
Erst geht man die Liste durch und gibt alle in ihr enthaltenen Objekte frei. Dann gibt man die Liste selber frei. Wichtig ist, dass die Schleife rückwärts laufen muss, da die Eingangsbedingung einer for-Schleife nur beim Eintritt in die Schleife geprüft wird, aber in der Schleife entfernen wir ja Elemente, so dass wir, wenn die Schleife vorwärts liefe, über die Anzahl der Elemente hinauslaufen würden.

Auslesen der Liste
Das Auslesen der Objekte ist dann eher trivial:
Delphi-Quellcode:
procedure TfrmMain.UpdateListBox;
var
  Contact          : TContact;
  i                : Integer;
  s                : string;
begin
  ListBox1.Items.Clear;
  for i := 0 to AddressBook.Contacts.Count - 1 do
  begin
    Contact := AddressBook.Contacts.Items[i];
    s := Contact.LastName + ', ' + Contact.FirstName;
    ListBox1.Items.Add(s);
  end;
end;
Wie man sieht wurde unsere Container-Klasse noch mal in einer weiteren Klasse TAddressBook gekapselt:
Delphi-Quellcode:
TAddressBook = class(TObject)
private
   FContacts: TContactList;
public
  constructor Create;
  destructor destroy; override;
  property Contacts: TContactList read FContacts;
end;
Dies dient nur dazu die Aufgaben der Klassen sauber zu trennen. Die Klasse TContactList dient nur dazu die Liste der Kontakte zu verwalten. Die Klasse TAddressBook hingegen nimmt später dann noch alle weiteren Methoden unseres Adressbuches auf.

Im Anhang das Demo-Projekt.

Edit: Vorschläge eingearbeitet.

Luckie 12. Mär 2007 20:52

Re: Verwalten von Objekten in einer Container-Klasse
 
Ich habe das ganze noch mal als PDF angehangen und das Tutorial steht jetzt auch als Artikel auf meiner Homepoage zur Verfügung: Verwalten von Objekten in einer Container-Klasse

Elvis 12. Mär 2007 21:44

Re: Verwalten von Objekten in einer Container-Klasse
 
Es gibt zwei Arten von Wiederverwendung in der OOP: Vererbung und Komposition.
Bei der Vererbung hat der neue Typ alle "Features" (Eifel-speak ;) ) des Vorgängers.
Bei der Komposition hält man eine Instanz eines anderen Types als Feld und verwendet diese Instanz.
Somit hat man eine bessere Kontrolle über das, was von außen verwendet werden kann.

Eine streng typisierte Liste durch Erben von TList ist ein Ding der Unmöglichkeit.
Du hast immer noch die Methode Add, welche einen Pointer nimmt und man kann immer noch einen Pointer auf TIrgendwas reinwerfen.
In deinem Beispiel wäre es also besser ein Feld vom Typen TObjectList herzunehmen, welches deine Daten hält. Aber nach außen bit du wirklich streng auf deine Elementenklasse typisiert.
Außerdem klingt eine Klasse namens "AddressBook" wie etwas, was mehr als nur ein paar Adressen enthält. Eher wie eine weitere Fachklasse, in der ein öhm Adressbuch modelliert ist.
Bleistift:
Delphi-Quellcode:
TContactList = class
private
  fInnerList : TObjectList;
  function GetItem(aIndex: Integer): TContact;
  procedure SetItem(aIndex: Integer; aItem: TContact);
protected
  property InnerList : TObjectList read fInnerList;
public
  destructor Destroy; override;
  procedure Add(aItem : TContact);
  procedure Delete(aIndex: Integer);
  property Items[aIndex: Integer]: TContact read GetItem write SetItem;
end;

TAddressBook = class
...
public
  property Contacts : TContactList read fContacts;
...

Luckie 12. Mär 2007 22:00

Re: Verwalten von Objekten in einer Container-Klasse
 
Deine Klassenaufteilung ist natürlich besser, jezt wo ich das so sehe. ;)

IngoD7 13. Mär 2007 08:52

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Elvis
Bleistift:
Delphi-Quellcode:
TContactList = class
private
  fInnerList : TObjectList;
  function GetItem(aIndex: Integer): TContact;
  procedure SetItem(aIndex: Integer; aItem: TContact);
protected
  property InnerList : TObjectList read fInnerList;
public
  destructor Destroy; override;
  procedure Add(aItem : TContact);
  procedure Delete(aIndex: Integer);
  property Items[aIndex: Integer]: TContact read GetItem write SetItem;
end;

Da fehlt aber der Konstruktor. Oder wo und wann würde man fInnerList erzeugen? :gruebel:

sh17 13. Mär 2007 09:21

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Elvis
Bleistift:
Delphi-Quellcode:
TContactList = class
private
  fInnerList : TObjectList;
  function GetItem(aIndex: Integer): TContact;
  procedure SetItem(aIndex: Integer; aItem: TContact);
...

Und wo ist jetzt der Vorteil gegenüber einer direkten Beerbung von TObjectList? Oder seh ich was nicht?

Daniel 13. Mär 2007 09:26

Re: Verwalten von Objekten in einer Container-Klasse
 
@Ingo:
Der Einwand ist sachlich korrekt, ich denke jedoch, dass es unserem King of Volksmusik mehr um das Prinzip ging.

@Sven:
Der Unterschied wird genau dann deutlich, wenn diese Klasse von außen genutzt wird. Die einzigen Schnittstellen zu dieser Liste gehen über die Getter und Setter, die für die Typensicherheit sorgen. Nur dadurch, dass die Liste selbst nicht nach außen hin sichtbar ist und damit die Nutzung von deren Add-Methode nicht möglich ist, ergibt die Typ-Sicherheit.

Elvis 13. Mär 2007 12:15

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Daniel
@Ingo:
Der Einwand ist sachlich korrekt, ich denke jedoch, dass es unserem King of Volksmusik mehr um das Prinzip ging.

Autsch! Das ging eindeutig unter die Gürtellinie! :evil: ( :mrgreen: )
Zitat:

@Sven:
Der Unterschied wird genau dann deutlich, wenn eine bessere Kontrolle über das haben will, was von außen verwendet werden kann.
...
Ganz genau, man kann selbst bestimmen was von außen verwendet werden kann[1].
Wenn du von einer generischen Containerklasse erbst, könnte man doch immer noch (unbeabsichtigt) die untypisierten Methoden dieses Vorgängers benutzen.

[1] Déja vu beabsichtigt, verwunderte Leser müssen also keinen Neurologen aufsuchen. :zwinker:

Luckie 13. Mär 2007 16:34

Re: Verwalten von Objekten in einer Container-Klasse
 
Ich habe es jetzt so:
Delphi-Quellcode:
type
  TContact = class(TObject)
  private
    FFirstName: String;
    FLastName: String;
    function GetFirstName: String;
    procedure SetFirstName(const Value: String);
    function GetLastName: String;
    procedure SetLastName(const Value: String);
  public
    property FirstName: String read GetFirstName write SetFirstName;
    property LastName: String read GetLastName write SetLastName;
  end;

  TContactList = class(TObject)
  private
    FInnerList: TList;
    function GetItem(Index: Integer): TContact;
    procedure SetItem(Index: Integer; Contact: TContact);
    function GetCount: Integer;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(Contact: TContact);
    procedure Delete(Index: Integer);
    property Count: Integer read GetCount;
    property Items[Index: Integer]: TContact read GetItem write SetItem;
  end;

  TAddressBook = class(TObject)
  private
     FContacts: TContactList;
  public
    constructor Create;
    destructor destroy; override;
    property Contacts: TContactList read FContacts write FContacts;
  end;
Aber hier:
Delphi-Quellcode:
procedure TContactList.Delete(Index: Integer);
begin
  // destroy object
  FInnerList.Items[Index].Free;
  FInnerList.Items[Index] := nil;
  // delete object from the list
  FInnerList.Delete(Index);
end;
sagt er mir in Zeile 4:
Zitat:

[Error] AddressBookCls.pas(106): Record, object or class type required
Er kennt also das Free nicht. Warum das nicht?

bernau 13. Mär 2007 17:00

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Luckie
Aber hier:
Delphi-Quellcode:
procedure TContactList.Delete(Index: Integer);
begin
  // destroy object
  FInnerList.Items[Index].Free;
  FInnerList.Items[Index] := nil;
  // delete object from the list
  FInnerList.Delete(Index);
end;
sagt er mir in Zeile 4:
Zitat:

[Error] AddressBookCls.pas(106): Record, object or class type required
Er kennt also das Free nicht. Warum das nicht?

TList.Items hat als Ergebnis den Type Pointer. Dort gibt's kein free.

Mach ein Typecasting

Delphi-Quellcode:
TObject(FInnerList.Items[Index]).Free;

Gerd

Luckie 13. Mär 2007 17:03

Re: Verwalten von Objekten in einer Container-Klasse
 
Auf die Idee bin ich natürlich nicht gekommen. :wall: Dank dir. Ich werde dann heute Abend mal den Artikel und das Tuorial hier überarbeiten und wieder einstellen.

Khabarakh 13. Mär 2007 17:51

Re: Verwalten von Objekten in einer Container-Klasse
 
Abgesehen von Deriving or Wrapping hat sich ein ganz allgemeiner Fehler eingeschlichen:

Zitat:

Zitat von Luckie
Delphi-Quellcode:
  if Assigned(Contact) then
    // ...
  else
    raise Exception.Create(rsObjectIsNil);

//...

  Self.Items[Index].Free;
  Self.Items[Index] := nil; // Hmm ;)


IngoD7 13. Mär 2007 20:41

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Luckie
Ich habe es jetzt so:
Delphi-Quellcode:
(...)
  TAddressBook = class(TObject)
  private
     FContacts: TContactList;
  public
    constructor Create;
    destructor destroy; override;
    property Contacts: TContactList read FContacts write FContacts;  //<------- write auch?? (IngoD7)
  end;

Frage:
Bist du sicher, dass du da die Referenzierung auf eine andere TContactList-Instanz ermöglichen möchtest?
Die Daten der im Konstruktor erzeugten Instanz von TContactList liegen danach im Nirvana.

Luckie 13. Mär 2007 22:47

Re: Verwalten von Objekten in einer Container-Klasse
 
Ups, war pure Gewohnheit.

@Khabarakh: Ich setze dort nur den Zeiger auf nil. Ist zwar an dieser Stelle wohl überflüssig, aber schaden sollte es nicht.

Khabarakh 14. Mär 2007 16:50

Re: Verwalten von Objekten in einer Container-Klasse
 
Generell schadet das Nillen nicht, aber wenn du im dadurch aufgerufenen Setter bei nil eine Exceptions wirfst, dann schadet es ;) .

Luckie 14. Mär 2007 16:52

Re: Verwalten von Objekten in einer Container-Klasse
 
Delphi-Quellcode:
procedure TContactList.SetItem(Index: Integer; Contact: TContact);
begin
  if Assigned(Contact) then
    FInnerList.Items[Index] := Contact
end;
:zwinker:

Thorben_K 14. Mär 2007 19:15

Re: Verwalten von Objekten in einer Container-Klasse
 
Ich hoffe du erlaubst mir die Frage, aber der Sinn von TAdressBock ist mir nicht klar.

Willst du damit nur verhindern das jemand die ContactList verändern kann, oder wie seh ich das?

Elvis 14. Mär 2007 19:31

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Khabarakh
Generell schadet das Nillen nicht, aber wenn du im dadurch aufgerufenen Setter bei nil eine Exceptions wirfst, dann schadet es ;) .

Erst wenn du nichts mehr aus deinem Code entfernen kannst bist du fertig damit.
Sinnloses nil-en von Werten, die eh aus dem Scope rennen (weil das Element nicht mehr in der Liste ist!), halte ich für mehr als überflüssig. ;)
Außerdem kannst du eine ObjectList mit OwnsObjects = true benutzen, und du musst dich damit nicht mehr befassen.


btw: Vielleicht sollte Michael den Thread mal auseinanderreisen: In das Tutorial und unser Fachchinesisch als "Diskussion zu ..."

IngoD7 14. Mär 2007 20:05

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Thorben_K
Ich hoffe du erlaubst mir die Frage, aber der Sinn von TAdressBock ist mir nicht klar.

Willst du damit nur verhindern das jemand die ContactList verändern kann, oder wie seh ich das?

So, wie TAdressBook da jetzt steht (also nur mit der Eigenschaft Contacts), hat es augenscheinlich keinen besonderen Nutzen. Da könnte man auch gleich eine TContactList instanziieren und verwenden.

Aber vielleicht spielt dieses Adressbuch ja beim Aufschlagen (programmtechnisch: im Konstruktor) ein Liedchen ab. Wer weiß ... ;)
Ein Adressbuch hat ja (später) vielleicht auch noch mehr Eigenschaften, wie z.B. Notizseiten und Registerblätter, etc. pp.

Dass also TAdressBook hier überhaupt - und noch so nackt - steht, dürfte nichts besonderes zu bedeuten haben.

Luckie 14. Mär 2007 21:48

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Elvis
Erst wenn du nichts mehr aus deinem Code entfernen kannst bist du fertig damit.

Schönes Zitat. Wuirst du jetzt zum Julius Cäsar der Programmierung: "Bello Programaticus" ;)

Zitat:

Sinnloses nil-en von Werten, die eh aus dem Scope rennen (weil das Element nicht mehr in der Liste ist!), halte ich für mehr als überflüssig. ;)
Gewohnheitssache. Wenn man sich so was einfach mal angewöhnt, kann man es nicht vergessen, wenn man es mal braucht.

Zitat:

Außerdem kannst du eine ObjectList mit OwnsObjects = true benutzen, und du musst dich damit nicht mehr befassen.
Und genau deshalb wollte ich sie in dem Tutorial nicht verwenden.

@Thorben_K: Im Augenblick wäre die Klasse TAddressBook natürlich noch überflüssig. Aber wenn man dann mal ein richtiges Adressbuch schreibt kommen ja noch mehr Dinge hinzu, die man dann dort sinnvoll kapseln kann.

sh17 15. Mär 2007 07:38

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von IngoD7
So, wie TAdressBook da jetzt steht (also nur mit der Eigenschaft Contacts), hat es augenscheinlich keinen besonderen Nutzen. Da könnte man auch gleich eine TContactList instanziieren und verwenden.

Das muss ich jetzt aber mal eine Lanze für Luckie brechen: Dieser Thread stellt ein Tutorial dar, wie man das betreffende Problem ideal umsetzt und keine Codebibliothek für eine Adressverwaltung. Die Klasse hätte ja auch TDideldum heissen können. Wer erinnert sich nicht an solch sinnvolle Aufgabenstellungen an der Uni: Erstellen Sie eine JAVA-Anwendung, die ein Flugbuchungssystem abbildet...

Zitat:

Zitat von Luckie
Und genau deshalb wollte ich sie in dem Tutorial nicht verwenden.

Warum nicht? Man muss es sich doch nicht unnötig schwer machen. Sonst können wir ja IHMO gleich Assembler schreiben.

Elvis 15. Mär 2007 08:33

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von sh17
Warum nicht? Man muss es sich doch nicht unnötig schwer machen. Sonst können wir ja IHMO gleich Assembler schreiben.

Naja, für ein Tutorial finde ich es auch sinnvoll nicht TObjectList zu nehmen.
Wobei man hier weiter gehen könnte und eine eigene Liste implementieren kann, die man dann anstatt TList verwendet.

sh17 15. Mär 2007 08:40

Re: Verwalten von Objekten in einer Container-Klasse
 
:duck: ok, ich halt mich raus

Elvis 15. Mär 2007 08:42

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von sh17
:duck: ok, ich halt mich raus

Pazifisten, pffft! :P

sh17 15. Mär 2007 08:45

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Elvis
Pazifisten, pffft! :P

:shock: :twisted: Ich schreib wieder was, wenn's spannend wird.

IngoD7 15. Mär 2007 09:03

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von sh17
Zitat:

Zitat von IngoD7
So, wie TAdressBook da jetzt steht (also nur mit der Eigenschaft Contacts), hat es augenscheinlich keinen besonderen Nutzen. Da könnte man auch gleich eine TContactList instanziieren und verwenden.

Das muss ich jetzt aber mal eine Lanze für Luckie brechen:

Das brauchst du nicht. Es hat niemand eine Lanze vor Luckie in den Boden gerammt.

Letztlich sieht er es ja genauso, wie seine Antwort an Thorben_K ein Posting später zeigt.
Sowohl seinen als auch meinen Ausführungen ist diesbezüglich nichts hinzuzufügen.

Zitat:

Zitat von sh17
Wer erinnert sich nicht an solch sinnvolle Aufgabenstellungen an der Uni: Erstellen Sie eine JAVA-Anwendung, die ein Flugbuchungssystem abbildet...

Ich. :)

Jens Schumann 15. Mär 2007 21:53

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Elvis
Eine streng typisierte Liste durch Erben von TList ist ein Ding der Unmöglichkeit.

Es könnte sein, das ich schon etwas eingerostet bin. Aber das sollte ein streng typisierter Nachfahre von
TList sein. Sieht eigentlich ganz einfach aus.
Delphi-Quellcode:
unit Unit2;

interface

uses classes;

Type

  TMyListItem = class(TObject)
  private
    FName: String;
    procedure SetName(const Value: String);
  public
    property Name : String read FName write SetName;
  end;

  TMyListItems = class(TList)
  protected
    function Get(Index: Integer): TMyListItem;
    procedure Put(Index: Integer; const Value: TMyListItem);
  public
    function Add : TMyListItem;
    procedure Clear; override;
    function Extract(Item: TMyListItem): TMyListItem;
    function First: TMyListItem;
    function IndexOf(Item: TMyListItem): Integer;
    procedure Insert(Index: Integer; Item: TMyListItem);
    function Last: TMyListItem;
    property Items[Index: Integer]: TMyListItem read Get write Put; default;
  end;

  implementation

{ TMyListItem }

procedure TMyListItem.SetName(const Value: String);
begin
  FName:=Value;
end;

{ TMyListItems }

function TMyListItems.Add: TMyListItem;
begin
  Result:=TMyListItem.Create;
  inherited Add(Result);
end;

procedure TMyListItems.Clear;
var
  iCnt : Integer;
begin
  For iCnt := 0 to Count - 1 do
    TObject(Items[iCnt]).Free;
  inherited Clear;
end;

function TMyListItems.Extract(Item: TMyListItem): TMyListItem;
begin
  Result:=TMyListItem(inherited Extract(Item));
end;

function TMyListItems.First: TMyListItem;
begin
  Result:=TMyListItem(inherited First);
end;

function TMyListItems.Get(Index: Integer): TMyListItem;
begin
  Result:=TMyListItem(inherited Get(Index));
end;

function TMyListItems.IndexOf(Item: TMyListItem): Integer;
begin
  Result:=inherited IndexOf(Item);
end;

procedure TMyListItems.Insert(Index: Integer; Item: TMyListItem);
begin
  inherited Insert(Index,Item);
end;

function TMyListItems.Last: TMyListItem;
begin
  Result:=TMyListItem(inherited Last);
end;

procedure TMyListItems.Put(Index: Integer; const Value: TMyListItem);
begin
  inherited Put(Index,Value);
end;

end.

Elvis 15. Mär 2007 22:15

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Jens Schumann
Zitat:

Zitat von Elvis
Eine streng typisierte Liste durch Erben von TList ist ein Ding der Unmöglichkeit.

Es könnte sein, das ich schon etwas eingerostet bin. Aber das sollte ein streng typisierter Nachfahre von
TList sein. Sieht eigentlich ganz einfach aus.

Keine Sorge, gegen Rost gibt es Mittelchen in jedem Baumarkt. :mrgreen:
Wäre deine Liste wirklich streng typisiert könnte ich das nicht machen:
Delphi-Quellcode:
var
  list     : TMyListItems;
  item     : TMyListItem;
  evilValue : PInteger;
begin
  ...
  new(evilValue);
  list.Add(evilValue);

  item := list[0];

  WriteLn(item.Name); //Hmpf? Was steht denn da drin?

Luckie 15. Mär 2007 22:46

Re: Verwalten von Objekten in einer Container-Klasse
 
Ich habe jetzt mal die Vorschläge eingearbeitet. Demo und PDF wurden auch aktualisiert.

OldGrumpy 16. Mär 2007 00:43

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Luckie
Erst wird das Objekt in der Liste freigegeben und dann aus der selbigen gelöscht. Ebenso verfährt man beim Freigeben der Container-Klasse:
Delphi-Quellcode:
destructor TContactList.Destroy;
var
  i                : Integer;
begin
  if FInnerList.Count > 0 then
  begin
    for i := FInnerList.Count - 1 downto 0 do
    begin
      TObject(FInnerList.Items[i]).Free;
    end;
  end;
  FInnerList.Free;
  inherited;
end;
Erst geht man die Liste durch und gibt alle in ihr enthaltenen Objekte frei. Dann gibt man die Liste selber frei. Wichtig ist, dass die Schleife rückwärts laufen muss, da die Eingangsbedingung einer for-Schleife nur beim Eintritt in die Schleife geprüft wird, aber in der Schleife entfernen wir ja Elemente, so dass wir, wenn die Schleife vorwärts liefe, über die Anzahl der Elemente hinauslaufen würden.

Ähm... also irgendwie passen diese Zeilen nicht zusammen... Du führst doch nur ein Free() aus und kein Delete() auf die einzelnen Objekte - die Schleife könnte also genauso gut vorwärts laufen. Isses schon zu spät oder hab ich gut aufgepasst? :mrgreen:

Luckie 16. Mär 2007 08:33

Re: Verwalten von Objekten in einer Container-Klasse
 
Du hast gut aufgepasst. ;) Es war gestern schon spät und ich habe im neuen Büro acht Stunden Wände weiß angemalt, das ist nichts für Programmierer. :mrgreen:

bernau 16. Mär 2007 08:52

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von OldGrumpy
die Schleife könnte also genauso gut vorwärts laufen.

Prinzipiell ist es doch egal, ob die Schleife vorwärts oder rückwärts läuft. Hauptsache die Items werden freigegeben.

Es sei denn, daß die Ausführungsgeschwindigkeit ggf. schneller ist, wenn's vorwärts läuft.

Preisfrage. Wer weis es :-)

bernau 16. Mär 2007 08:59

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von Elvis
Delphi-Quellcode:
var
  list     : TMyListItems;
  item     : TMyListItem;
  evilValue : PInteger;
begin
  ...
  new(evilValue);
  list.Add(evilValue);

  item := list[0];

  WriteLn(item.Name); //Hmpf? Was steht denn da drin?


Ich hab'S nicht getestet, aber...

Delphi-Quellcode:
  list.Add(evilValue);

Hier müstest du eigendlich einen Compilerfehler bekommen, da die Function Add überschrieben wurde und nur noch ein Ergebnis zurückliefert und keine Parameter erwartet.



Gerd

OldGrumpy 16. Mär 2007 16:02

Re: Verwalten von Objekten in einer Container-Klasse
 
@bernau, lies nochmal genau das Tutorial, dann findest Du sicher die Begründung warum es eben NICHT immer egal ist :mrgreen:

@luckie: Ja, das kommt mir bekannt vor *g* In erster Linie gings mir darum, zu wissen ob ich was übersehen hätte oder nicht. :)

bernau 16. Mär 2007 22:06

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von OldGrumpy
@bernau, lies nochmal genau das Tutorial, dann findest Du sicher die Begründung warum es eben NICHT immer egal ist :mrgreen:

Solche Antworten veranlassen mich eigendlich immer sofort eine Diskussion abzubrechen und nicht mehr zu antworten.:evil: Ich kommer mir dabei immer vor wie im Kindergarten. Warum kann man es nicht auf den Punkt bringen und folgendermassen antworten:

Zitat:

Zitat von So wünsche ich mir die Antwort
@bernau, lies nochmal genau das Tutorial, im Abschnitt "Den Speicher verwalten" im dritten Absatz steht steht die Begründung warum es eben NICHT immer egal ist :mrgreen:

Ich kann leider keine Gedanken lesen. Deshalb kann ich nur vermuten, daß du diesen Absatz meinst.

Erst geht man die Liste durch und gibt alle in ihr enthaltenen Objekte frei. Dann gibt man die Liste selber frei. Wichtig ist, dass die Schleife rückwärts laufen muss, da die Eingangsbedingung einer for-Schleife nur beim Eintritt in die Schleife geprüft wird, aber in der Schleife entfernen wir ja Elemente, so dass wir, wenn die Schleife vorwärts liefe, über die Anzahl der Elemente hinauslaufen würden.

Nur ist diese Aussage schlichtweg falsch. In dem Beispiel werden in der Schleife lediglich die Objekte freigegeben. Die Menge der Elemente verändert sich nicht, da kein Delete aufgerufen wird. Daher ist es tatsächlich egal, ob die Schleife vorwärts oder rückwärts läuft. Wie du es ja selber geschrieben hast:

Zitat:

Zitat von OldGrumpy
Du führst doch nur ein Free() aus und kein Delete()

Es ist schon klar, daß man eine Schleife nicht vorwärts laufen lassen darf, wenn in der schleife die Elemente durch ein Delete entfernt werden.

Aber ich habe mich ja auf das Beispiel von Luckie bezogen. Und bei diesem Beispiel ist es tatsächlich egal ob vorwärts oder rückwärts. Einen Fehler wird's nicht geben. Mich hat eben nur interessiert ob es für dieses Beispiel tatsächlich einen Grund geben könnte, die Schleife in eine bestimmte Richtung laufen zu lassen.

Der einzige Grund hierbei rückwärts zu zählen ist IMHO, daß eine Prüfung des Schleifenzählers auf 0 schneller verarbeitet wird, als die Prüfung, ob der Schleifenzähler eine bestimmte Zahl erreicht hat. Aber ob daß tatsächlich ein ausschlaggebender Punkt ist, bezweifele ich.

Aber vieleicht gibt's ja tatsächlich noch andere Gründe.


Gerd

OldGrumpy 18. Mär 2007 10:47

Re: Verwalten von Objekten in einer Container-Klasse
 
@bernau:

Erstens: War schon spät
Zweitens: Warum soll ich Dir alles vorkauen, hast doch die Stelle auch selber gefunden. Ich unterstütze Denkfaulheit nicht.
Drittens: Ich schrieb "nicht *immer*" - Luckie hatte ja bereits geschrieben dass das etwas anders geplant war, und weiter oben im Tutorial gibts genau so eine Stelle wo alle Objekte abgeräumt werden - "schlicht falsch" ist meilenweit daneben.
Viertens: Damit Du nicht wieder ne Beschwerde posten musst, hier noch die Info dass ich mich auf TContactList.Delete() aus dem Tutorial beziehe.
Fünftens: Schönen Sonntag noch! :cheers:

bernau 18. Mär 2007 11:35

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von OldGrumpy
Zweitens: Warum soll ich Dir alles vorkauen, hast doch die Stelle auch selber gefunden. Ich unterstütze Denkfaulheit nicht.

Das hat nichts mit Denkfaulheit zu tun. Es hilft nur ungemein, wenn man weis worauf sich ein Kommentar bezieht.

Zitat:

Zitat von OldGrumpy
Viertens: Damit Du nicht wieder ne Beschwerde posten musst, hier noch die Info dass ich mich auf TContactList.Delete() aus dem Tutorial beziehe.

Was allerdings nichts mit der besagten Schleife zu tun hat.:zwinker:

Zitat:

Zitat von OldGrumpy
Fünftens: Schönen Sonntag noch! :cheers:

Jo. Lass uns wieder Freunde sein. :kiss:

SvB 17. Apr 2007 14:48

Re: Verwalten von Objekten in einer Container-Klasse
 
Hallo,

sehr interessant, da ich auch gerade so was brauche und auch einiges schon selbst sehr ähnlich gemacht habe.
Um mein Verständnis etwas zu erweitern, bzw. Euren Rat einzuholen folgende Frage / Erweiterung - ist das so OK:
Kann ich Methode wie folgt implementieren. Alles was im Beispiel gleich ist, habe ich jetzt hier nicht mehr reingepakt.

Delphi-Quellcode:
TContactList = class(TObject)
private
  FOnContactAdded: TNotifyEvent;
public
  destructor Destroy; override;
  function AddContact: TContact;
  procedure DeleteAllItems;
end;

TContactList.AddContact: TContact;
begin
  Result := TContact.Create(self);
  FInnerList.Add(Result);
  if Assigned(FOnContactAdded) then FOnContactAdded(Result);
end;

TContactList.DeleteAllItems;
var
  i : Integer;
begin
  if FInnerList.Count > 0 then begin
    for i := FInnerList.Count - 1 downto 0 do begin
      TObject(FInnerList.Items[i]).Free;
    end;
  end;
  FInnerList.Clear;
  // Würde FInnerList.Clear nicht auch alle Objekte freigeben?
end;

destructor TContactList.Destroy;
begin
  DeleteAllItems;
  FInnerList.Free;
  inherited;
end;
Was meint Ihr?

Grüße
Sven

bernau 17. Apr 2007 15:29

Re: Verwalten von Objekten in einer Container-Klasse
 
Zitat:

Zitat von SvB
// Würde FInnerList.Clear nicht auch alle Objekte freigeben?

Nur wenn FInnerList.OwnsObjects = true. Ansonstem muss man sich selber um die Freigabe der Objekte kümmern.


GErd

Elvis 17. Apr 2007 15:39

Re: Verwalten von Objekten in einer Container-Klasse
 
Nimm' als InnerList eine TobjectList mit OwnsObject = true.
Und zum löschen, rufst du einfach ihr Clear auf. (Du nennst es dann natürlich auch Clear ;) ).
Falls du einen event auslösen willst, der dich darüber informiert, dass ein Item entfernt wurde, müsste du natürlich wieder rückwärts durchlaufen und alles selbst rauswerfen.
Der Cast zu TObject ist natürlich witzlos, wenn es eine TObjectList ist.

btw: Dupliziere nix was bereits TObjectList implementiert hat, reiche es nur durch.
Code, den du nicht geschrieben hast, musst du auch nicht testen. (wenn er so oft benutzt wird wie TObjectList.Xyz, ist ja fast wie TObject.Free() ;) )


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:40 Uhr.

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