uses System.Generics.Collections;

uses System.Generics.Collections;
TFoo = array of Integer;
TBar = class(TList);

Note that I have defined more than 1000 Integer constants (ActionIDs) and want to partition the whole set into groups or categories, in different fashions. I experimented a little and came up with something that compiles. It is convenient to edit the groups in the code editor where the design work happens. But it does not work at runtime. I cannot retrieve the Integer values as expected from the List. Why? Debugging is difficult. So please explain.

Code:

program IntGroupTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
System.SysUtils,
System.Classes,
System.Generics.Collections;

type
TActionGroup = array of Integer;

TActionGroupList = class(TList)
public
constructor Create;
end;

const
Action1 = 1;
Action2 = 2;
Action3 = 3;
Action4 = 4;
Action5 = 5;
Action6 = 6;

ActionGroup1: TActionGroup = [
Action1,
Action2,
Action3
];

ActionGroup2: TActionGroup = [
Action4
];

ActionGroup3: TActionGroup = [
Action5,
Action6
];

constructor TActionGroupList.Create;
begin
inherited;
Add(ActionGroup1);
Add(ActionGroup2);
Add(ActionGroup3);
end;

var
AG: TActionGroup;
AGL: TActionGroupList;

begin
try
AGL := TActionGroupList.Create;

//this does not work.
AG := AGL.Items[0];
WriteLn(Format('1 = %d', [AG[0]]));
WriteLn(Format('2 = %d', [AG[1]]));
WriteLn(Format('3 = %d', [AG[2]]));

//this is ok.
WriteLn('');
WriteLn(Format('1 = %d', [ActionGroup1[0]]));
WriteLn(Format('2 = %d', [ActionGroup1[1]]));
WriteLn(Format('3 = %d', [ActionGroup1[2]]));

ReadLn;

except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

Output:

1 = 1
2 = 39069312
3 = 1

1 = 1
2 = 2
3 = 3

Comments

  1. I think, because it's a dynamic array, the TList<> doesn't reserve the memory like you expect. This seems to work fine:
    constructor TActionGroupList.Create;
    var
    AG: TActionGroup;
    begin
    inherited;
    AG := ActionGroup1;
    Add(AG);
    AG := ActionGroup2;
    Add(AG);
    AG := ActionGroup3;
    Add(AG);
    end;

    ReplyDelete
  2. Yes, this works. So: Add does not like global constants or global vars, only local vars. Hmm.

    constructor TActionGroupList.Create;
    begin
    inherited;
    AddSpecial(ActionGroup1);
    AddSpecial(ActionGroup2);
    AddSpecial(ActionGroup3);
    end;

    procedure TActionGroupList.AddSpecial(const Value: TActionGroup);
    var
    AG: TActionGroup;
    begin
    AG := Value;
    Add(AG);
    end;

    ReplyDelete
  3. Ouch! The bug is in TListHelper.DoAddDynArray which uses that nasty hardcast to TBytes which causes it to work wrong if the passed array is a const one (refcount = -1) (see System._DynArrayAsg)

    Please report this!

    P.S. I guess it works wrong regardless because in all cases it calls _DynArrayClear(Dest, TypeInfo); with TypeInfo being TypeInfo(TBytes) instead of the correct type.

    ReplyDelete

Post a Comment