views:

265

answers:

2

I'm going slightly mad (singing Queen's song) about records with Generic Containers (TList). First, see this code:

  TItemRec = record
  private
    FSender    : TAction;
    FOwnerPack : HModule;
    FDockPanel : TdxDockPanel;
    procedure SetDockPanel(const Value: TdxDockPanel);
    procedure SetOwnerPack(const Value: HModule);
    procedure SetSender(const Value: TAction);
  public
    property Sender: TAction read FSender write SetSender;
    property OwnerPack: HModule read FOwnerPack write SetOwnerPack;
    property DockPanel: TdxDockPanel read FDockPanel write SetDockPanel;
  end;

  TPackRec = record
  private
    FHandle  : HModule;
    var FCounter : Int16;
    procedure SetCounter(const Value: Int16);
    procedure SetHandle(const Value: HModule);
  public
    property Handle: HModule read FHandle write SetHandle;
    property Counter: Int16 read FCounter write SetCounter;
    procedure ChangeCounter(IncValue: Boolean = True);
  end;

  TRecList = class(TList<TItemRec>)
  public
    procedure CloseDockPanels;
  end;

  TPackList = class(TList<TPackRec>)
  strict private
    procedure DoChanges(const APack: HModule; const AAdd: Boolean = True);
  public
    procedure CheckForUnusedItems;
    procedure AppendRef(const APack: HModule);
    procedure DeleteRef(const APack: HModule);
    procedure ClosePackages;
  end;

  TfrmMain = class(TForm)
  private
    FRecList  : TRecList;
    FPackList : TPackList;
  public
  end;

........................

procedure TPackList.CheckForUnusedItems;
var
  i : Int16;
  Flag : Boolean;
begin
  repeat
    Flag:= False;
    if Self.Count > 0 then begin
      for i:= Self.Count - 1 downto 0 do begin
        Flag:= Self.Items[i].FCounter > 0;
        if not Flag then begin
          Self.Delete(i);
          Flag:= True;
          Break;
        end;
        Flag:= False;
      end;
    end;
  until not Flag;
end;

procedure TPackRec.ChangeCounter(IncValue: Boolean);
var
  Value : Int16;
begin
  Value:= Counter;
  if IncValue then
    Value:= Value + 1
  else
    Value:= Value - 1;
  Counter:= Value;
end;

I have a serious problem when trying to change value for Counter property, which is the only item with a changing value. ChangeCounter method should change value of Counter, and apparently it is done, but for CheckForUnusedItems method changed nothing, I've tried almost everything but it seems that each of TPackList and TRecList was a constant. I show you some pics:

Items added successfully

Pic01

Item of FRecList deleted

Pic02

ChangeCounter method changed value of Counter

Pic03

CheckForUnusedItems method doesnot see any change

Pic04

Counter property keeps its value like a constant

Pic05

What is happening? Is there an explanation for this? How can I resolved it? Thanks in advance.

A: 

Ok, first, you don't need the var before FCounter, but it does not do any harm.

Next, because ChangeCounter is a method of the record that contains the value FCounter, why not use:

procedure TPackRec.ChangeCounter(IncValue: Boolean);
begin
  if IncValue then
    Inc(FCounter)
  else
    Dec(FCounter);
end;

You can even add Overflow/underflow checking if you like.

There are some problems with properties in records. Especially if the record is returned by a function. Assigning the record to a local variable solves this problem.

Gamecat
+6  A: 

The default indexer property of TList returns the copy of the value inside the container; then you call ChangeCounter on that copy, and it changes the field of that copy. The original item inside the container didn't change. If you want it to work, you have to store it in a local, call any mutating methods on that local, and then store the local back into the container, overwriting the old value.

This holds true of any container other than arrays, as there's no way to "return by reference", so that further mutating operations on the returned value are reflected in the container itself.

Pavel Minaev
Yep! The reason this works fine for objects is because objects are reference types, and so all that gets copied is the pointer to the object. This isn't the case for records.
Mason Wheeler