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
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
I think, because it's a dynamic array, the TList<> doesn't reserve the memory like you expect. This seems to work fine:
ReplyDeleteconstructor TActionGroupList.Create;
var
AG: TActionGroup;
begin
inherited;
AG := ActionGroup1;
Add(AG);
AG := ActionGroup2;
Add(AG);
AG := ActionGroup3;
Add(AG);
end;
Yes, this works. So: Add does not like global constants or global vars, only local vars. Hmm.
ReplyDeleteconstructor 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;
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)
ReplyDeletePlease 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.