Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi TList und record: Auf Variablen zugreifen (https://www.delphipraxis.net/76570-tlist-und-record-auf-variablen-zugreifen.html)

Sascha L 6. Sep 2006 11:41


TList und record: Auf Variablen zugreifen
 
Hallo,

habe folgenden Code:

Delphi-Quellcode:
TFeedItem = record
    Title,
    Description,
    Author,
    URL          : String;
    DateTime     : TDateTime;
  end;

  TFeedItemClass = class(TPersistent)
    private
      fItems: TList;
      function GetItem(Index: Integer): TFeedItem;
      procedure SetItem(Index: Integer; const FeedItem: TFeedItem);
      function GetCount: Integer;
    public
      constructor Create;
      destructor Destroy; override;
      procedure AddItem(const FeedItem: TFeedItem);
      procedure RemoveItem(Index: Integer);
      procedure Clear;
      property Items[Index: Integer]: TFeedItem read GetItem write SetItem;
      property Count: Integer read GetCount;
  end;

function TFeedItemClass.GetItem(Index: Integer): TFeedItem;
begin
  Result := TFeedItem(fItems[Index]^);
end;

procedure TFeedItemClass.SetItem(Index: Integer; const FeedItem: TFeedItem);
begin
  fItems[Index] := @FeedItem;
end;
Nun ist es möglich, dass ich z.B. folgendes machen kann:

Delphi-Quellcode:
var
  Item: TFeedItem;
begin
  Item.Title := 'sdfsdf';
  FeedItemClass.Items[5] := Item;
end;
Damit würde das 5. Item mit den neuen Daten überschrieben werden. (In diesem Beispiel existiert das 5. Item natürlich!)

Es ist aber nicht möglich folgendes zu machen:

Delphi-Quellcode:
  FeedItemClass.Items[5].Title := 'sdfsdfs';
Da meckert der Compiler, dass auf Title nur lesend zugegriffen werden kann.

Wo muss ich nun was anpassen, damit ich normal auf die Variablen zugreifen kann?

Gruß
Sascha

KrasserChecker 6. Sep 2006 11:49

Re: TList und record: Auf Variablen zugreifen
 
Du solltest nie "records" als Properties verwenden. durch die interne Speicherverwaltung wird nämlich nie auf die eigentlichen Speicherbereiche verwiesen, sondern nur auf Kopien.
Daher empfehel ich dir die Struktur "TFeedItem" in eine Klasse umzuwandeln & in der Klasse "TFeedItemClass" statt "TList" "TObjectList" zu verwenden.
"TObjectList" verwaltet Objekte - sprich gibt deren Speicherbereiche auch korrekt wieder frei.

marabu 6. Sep 2006 11:57

Re: TList und record: Auf Variablen zugreifen
 
Hallo Sascha,

die korrekte Art des Zugriffs hast du selbst schon gezeigt. Das Problem beim direkten Setzen von Title ist die Tatsache, dass du gar nicht auf einen record zugreifst, sondern auf eine function - nur die Notation .Items[5]. gaukelt dir den Zugriff auf eine Struktur TFeedItem vor.

Delphi-Quellcode:
var
  // ...
  FeedItem: TFeedItem;
begin
  // ...
  FeedItem := FeedItemClass.Items[5];
  FeedItem.Title := 'sdfsdfs';
end;
Grüße vom marabu

Sascha L 6. Sep 2006 12:09

Re: TList und record: Auf Variablen zugreifen
 
Danke ;)

Ich habe es nun aus TFeedItem eine Klasse gebaut und nun klappt es auch alles einwandfrei :)

Nur das erste Beispiel funktioniert nur noch teilweise, was auch logisch ist:

Delphi-Quellcode:
var
  Item: TFeedItem;
begin
  Item := TFeedItem.Create;
  Item.Title := 'sdfsdf';
  FeedItemClass.Items[5] := Item;
end;
Das klappt nun. Muss ich aber nun nicht selbst noch Item wieder freigeben, oder wird das später automatisch gemacht? Denn wenn ich am Ende des Codes Item.Free mache, dann ist es auch unter Items[] nicht mehr drin bzw. nur noch leere Strings. Reicht es aus, wenn ich in der SetItem-Methode eine neue Klasse erzeuge und das übermittelte Item dort übergebe, sodass ich es außerhalb auch ohne Probleme freigeben kann?

edit: ok, es reicht nicht aus bzw. klappt so auch nicht...

hoika 6. Sep 2006 16:45

Re: TList und record: Auf Variablen zugreifen
 
Hallo,

ich hatte mir das damals einfacher gemacht,
indem ich eine eigene Listen-Klasse (TListEx)
und eine Basisklasse für alle Einträge dieser Liste
gebaut habe (TListItemEx).

Im destructor der TListEx steht dann

Delphi-Quellcode:
{ Listenelemente freigeben }
destructor TListEx.Destroy;
begin
  try
    ClearList;
    inherited Destroy;
  except
  end;
end; { TListEx.Destroy }


procedure TListEx.ClearList;
var
  iItem : Integer;
  Item : TListItemEx;
begin
  try
    for iItem:= Count-1 downto 0 do
    begin
      Item:= Items[iItem];
      Delete(iItem);

     { free, if we have the original pointer here }
      if (bHasLinks=False) then
      begin
        FreeAndNIL(Item);
      end;
    end
  except
  end;
end;
Alle Klassen werden von TListItemEx abgeleitet.

#edit#
FreeAndNil kann auch durch Item.Free ersetzte werden.
Der Destructor ist in Wirklichkeit aber etwas länger, ich benutze das NIL.
Das bHasLinks ist ien property, was wenn es True ist, die Klasse selber nicht freigibt,
sondern nur aus der Liste löscht.
Heiko

KrasserChecker 6. Sep 2006 18:01

Re: TList und record: Auf Variablen zugreifen
 
Wie bereits geschrieben verwaltet TObjectList die enthaltenen Objekte. D.h., dass sie beim Freigeben alle enthaltenen Objekte mit freigibt. Du brauchat (und darfst) die Objekte vom Typ "TFeedItem" NICHT selbst freigeben (wenn sie einmal in der TObjectList enthalten sind).

Die Methode von oben würde wie folgt aussehen:

Delphi-Quellcode:
var
  Item: TFeedItem;
begin
  Item := TFeedItem.Create;
  Item.Title := 'sdfsdf';
  FeedItemClass.AddItem(Item);
end;
Außerdem würde ich die Methode "SetItem" entfernen, da Du dir mit soetwas evt. die Zeiger auf ein bereits vorhandenes Objekt überschreibst. Dessen Speicherbereich ist dann "vergessen" und vor allem: verloren.

Die Methode "GetItem" sieht natürlich so aus:

Delphi-Quellcode:
function TFeedItemClass.GetItem(Index: Integer): TFeedItem;
begin
  Result := FItems[Index] as TFeedItem;
end;

schöni 7. Nov 2006 18:08

Re: TList und record: Auf Variablen zugreifen
 
Hallo!

Ich habe hier das Problem, das ich einerseits in der Compare_ Funktion eine EInvalidOp Exception erhalte, andererseits auch noch eine EInvalidOp

Delphi-Quellcode:
type
  PObjStruct = ^TObjStruct;
  TObjStruct = record
    useability,mass,optimum: Extended;
  end;
  TObjects = class(TList)
    function Add(Item: Pointer): Integer;
  end;

function TObjects.Add(Item: Pointer): Integer;
begin
   PObjStruct(Item).optimum := PObjStruct(Item)^.useability / PObjStruct(Item)^.mass;
   Result := inherited Add(PObjStruct(Item));
end;

function Compare_(Item1,Item2: Pointer): Integer;
begin
 if PObjStruct(Item1)^.optimum < PObjStruct(Item2)^.optimum then Result := -1 else
 if PObjStruct(Item1)^.optimum > PObjStruct(Item2)^.optimum then Result := +1 else //EInvalidOp in der zweiten Vergleichsoperation
 Result := 0;
end;

procedure Optimize(var AObjects,AList: TList; AMaxMass: Extended);
var i,j: Integer; amass: Extended;
begin
  amass := 0.0;
  AList.Sort(Compare_);
  i := AList.Count-1;
  while i > 0 do
  begin
    amass := amass + PObjStruct(AList.Items[i])^.mass; //EInvalidOp
    if amass<=AMaxMass then AObjects.Add(PObjStruct(AList.Items[i])) else
    begin
      j:=i;
      while (amass>AMaxMass) and (j>0) do
      begin
        amass := amass - PObjStruct(AList.Items[j])^.mass;
        amass := amass + PObjStruct(AList.Items[j-1])^.mass;
        if amass<=AMaxMass then AObjects.Add(PObjStruct(AList.Items[j-1]));
        Dec(j);
      end;
    end;
    Dec(i);
  end;
end;
Ich habe mich hier mal drangemacht, das Rucksackproblem zu lösen, das ein anderes Mitglied der DP hier erfragt hatte als ich vor einigen Tagen seit langem mal wieder hier vorbeigeschaut habe. Die Procedure Optimize hat folgende Parameter:

AObjects: Die fertige Liste mit den Objekten, die die Gewichtsschranke einhalten
AList: Die Liste mit den Objekten, aus denen passende ausgewählt werden
AMaxMass: Die Gewwichtsschranke

Leider hänge ich nun fest am Probelem des Zugriffs auf die Recordvariablen in der liste.
Wer kann mir hier helfen? Sobald das Problem gelöst ist, soll die Optimize-Prozedur hier in die CodeLibrary aufgenommen werden.

schöni

schöni 7. Nov 2006 18:52

Re: TList und record: Auf Variablen zugreifen
 
Habe in der Optimize Prozedur die Listentypen auf TObjects gesetzt. Nun Klappt's.


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