English 中文(简体)
Records and Generic Containers in Delphi
原标题:

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.

最佳回答

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.

问题回答

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.





相关问题
Silverlight: Add same UserControl to more than one Canvas

I have a bunch of UserControls ( MyUserControl ) that I want to have a user manually add to one or more Canvases. One instance of a UserControl cannot be a child element of more than one container (...

c++ templated container scanner

here s today s dilemma: suppose I ve class A{ public: virtual void doit() = 0; } then various subclasses of A, all implementing their good doit method. Now suppose I want to write a function ...

Multi-Dimensional Container in Java

I searched around on Google, but I was unable to find any libraries for a multi-dimensional container in Java (preferably one that supports generics as well). I could easily write one (in fact, I ...

c# stack queue combination

is there in C# some already defined generic container which can be used as Stack and as Queue at the same time? I just want to be able to append elements either to the end, or to the front of the ...

Custom container requirement to work with Qt s foreach

What is the bare minimum amount of code to create a custom container that would work with Qt foreach macro? I have this so far template< class T > class MyList { public: class iterator { ...

Having many stacks with different types

I m making a C program that needs to use two stacks. One needs to hold chars, the other needs to hold doubles. I have two structs, node and stack: struct node { double value; struct node *...

热门标签