Example:
var
mylist:Tmylist;
item:Tmylistitem;
begin
mylist:=Tmylist.create;
item:=mylist.add;
item.field1:=1;
item.field2:='A';
item:=mylist.add;
item.field1:=1;
item.field2:='A';
......
for idx:=0 to mylist.count-1 do
showmessage(mylist[idx].field2);
is there a simply way to create this kind of structure ?
thank you in advance
Roberto
--
> I want to create a list of items where each item is made of some fields
<snip>
> is there a simply way to create this kind of structure ?
There are many different ways to do that. The simpliest is to derive from
the TCollection and TCollectionItem classes. That way, all of the memory
for the items is handled for you automatically, and you also get to use the
exact syntax you showed when using the list. For example:
type
Tmylistitem = class(TCollectionItem)
public
field1: Integer;
field2: String;
end;
Tmylist = class(TCollection)
private
function GetmyItem(Index: Integer): Tmylistitem;
procedure SetmyItem(Index: Integer; Value: Tmylistitem);
public
constructor Create; reintroduce;
function Add: Tmylistitem; reintroduce;
property Items[Index: Integer]: Tmylistitem read GetmyItem write
SetmyItem; default;
end;
constructor Tmylist.Create;
begin
inherited Create(Tmylistitem);
end;
function Tmylist.Add: Tmylistitem;
begin
Result := Tmylistitem(inherited Add);
end;
function Tmylist.GetmyItem(Index: Integer): Tmylistitem;
begin
Result := Tmylistitem(inherited GetItem(Index));
end;
procedure Tmylist.SetmyItem(Index: Integer; Value: Tmylistitem);
begin
inherited SetItem(Index, Value);
end;
Gambit
> I want to create a list of items where each item is made of some
> fields
Your subject line specifically mentions records which are the old-style
non-OO way of encapsulating data. If you truly need such records then
it's a bit tricky; you'd likely need to allocate them dynamically and
store pointers to the records in a list.
If you can use a class instead of a record it's much easier. Remy
suggested one possibility. Another is to use a tList or tObjectList
like so:
type
tMyItem = class
public
field1 : integer;
field2 : string;
end;
var
list : tList;
item : tMyItem;
begin
list := tList.Create;
item := tMyItem.Create;
item.field1 := 1;
item.field2 := 'A';
list.add(item);
In this example you'd be responsible for making sure that the list and
all of the items it contains gets properly freed. With a tObjectList
you can specify that the list itself owns the items and will therefore
free the items when the list itself is freed.
--
-Mike (TeamB)
You could perhaps inherit from TCollection and TCollectionItem. I'm not
sure I would ever choose that approach, though.
This cries for generics, but they are not implemented in Win32 yet.
Currently I would do this:
type
PMyListItem = ^TMyListItem;
var
list: TList;
item: PMyListItem;
begin
list := TList.Create;
New(item);
item.field1 := 1;
item.field2 := 'A';
List.Add(item);
New(item);
item.field1 := 17;
item.field2 := '@';
List.Add(item);
And you can use it this way:
for i := 0 to list.Count - 1 do
ShowMessage(PMyListItem(list[i]).field2);
But you will have to take care of Dispose()ing all items in the list
before freeing the list. The list can't do that:
for i := 0 to list.Count - 1 do
Dispose(PMyListItem(list[i]));
list.Free;
Of course, if the list doesn't have to be expanded, you could also use
a dynamic array of TMyListItem. The array will take care of finalizing
the MyListItems.
--
Rudy Velthuis [TeamB]
"Despite the high cost of living, it remains popular."
> Roberto Nicchi wrote:
>
> > I want to create a list of items where each item is made of some
> > fields
>
> Your subject line specifically mentions records which are the
> old-style non-OO way of encapsulating data.
And the hip, new way of encapsulating data as well, since records can
now also have methods, a constructor and even operators. They are ideal
for types that are to be treated as value types. <g>
--
Rudy Velthuis [TeamB]
"Anything that is too stupid to be spoken is sung."
-- Voltaire (1694-1778)
The simple way (works in both Win32 and .NET single source) with
typesafety see sample code below.
There is a more difficult way, win32 only with
TCollection/TCollectionItem, sample can be found in Help Files (if you
have D7 or before that is, else, let's hope D2007 help brings that one
back).
Also if .NET is your only concern, as of Rad Studio 2007 release last
week, you can use generics.
***** Warning ******
Please be aware, this is an extreme simple sample, it does provide some
type safety, but it is far from efficient, optimize where needed when
needed.. You could for example also instead of creating a whole new
class, inherit from TList/TThreadList and override what you need.
Code is typed in plain text editor, not tested using compile...
Could be an typo here and there..
Disclaimer:
Code is provided as is, in no way form or shape, author can be held
responsible for damages incurring from using this code sample in any
way, shape or form...
***** Warning ******
....
type
TMyListException = class(Exception);
TMyListItem = Class(TObject)
private
fField1: Integer;
fField2: String;
fName: String;
public
constructor Create; // creates object and sets default values.
Destructor Destroy; override; // destroys object, only really
//need to override if you create objects in create.
published
{ Very simplified field handling here, better use
getters/setters
}
property Name: string read fName write fName;
property Field1: Integer read fField1 write fField1;
property Field2: String read fField2 write fField2;
end;
Class TMyList(TObject)
private
fList: TList; // or use TThreadList if it must be thread safe.
..
protected
function GetItem(index: integer):TMyListItem;
function GetCount: integer;
public
constructor Create;
destructor Destroy; override;
procedure Clear; // remove all items from the list.
function Add: TMyListItem; overload; // create new item and add.
procedure Add(aItem: TMyListItem); overload; // add an item.
function ItemByName(aName: String): TMyListItem; // find item.
property Items[Index: Integer]:TMyListItem read GetItem; default;
// list of items to be accessed directly.
published
property Count: Integer read GetCount; // get number of items.
end;
....
Constructor TMyList.Create;
begin
inherited Create;
fList := TList.Create; // use TThreadList if threadsafety is needed,
// must also adapt other functionality to be able to do this tho.
end;
destructor TMyList.Destroy;
begin
if assigned(fList) then
begin
Clear;
fList.Free;
end;
inherited Destroy;
end;
procedure TMyList.Clear;
var Obj: TObject;
begin
for Obj in fList do
TMyListItem(Obj).Free; // i know dangerous casting here.
// but since the add functions ensure type, not so very dangerous.
// Maybe check if Obj is really assigned, but you could also ensure
// that in add methods.
end;
function TMyList.Add: TMyListItem;
begin
result := TMyListItem.Create; // create a new item.
Add(Result);
{
You could add directly to fList by doing fList.Add(result) but you
might still want to call self.add if you do more than just simply
adding to the list, saves you from having to write that code
twice,
and prevent unexplicable bugs..
}
end;
procedure TMyList.Add(aItem: TMyListItem);
begin
fList.Add(aItem); // simply add to the list.
end;
// get an item by its name.
function TMyList.ItemByName(aName: string):TMyListItem;
var Obj: TObject;
begin
for Obj in fList do
if TMyListObject(Obj).Name = aName then // again casting
// also very slow compare, also case sensitive.
begin
result := TMyListObject(Obj); // alas again casting.
break;
end;
end;
function TMyList.GetItem(index: integer):TMyListItem;
var n: integer;
begin
n := Count;
if (index >= 0) and (index < n) then
result := TMyListObject(fList[index])
else
raise TMyListException.Create('e - Index out of Range.');
end;
From here on you can use TMyList.Create to create an instance of your
list, and use it in a way as you described.
--
Marco Caspers..
> From here on you can use TMyList.Create to create an instance of your
> list, and use it in a way as you described.
I would, in Delphi for Win32, derive from TObjectList, and in Delphi
for .NET, from ArrayList, or use one as aggregate. Would make class
design a little easier, and you don't have to take care of handling the
list internals, only of casting to the appropriate types on output.
Generics were actually designed to make doing the above, or what you
do, unnecessary for each new element type of a list. <g>
--
Rudy Velthuis [TeamB]
"Nothing is wrong with California that a rise in the ocean level
wouldn't cure."
-- Ross MacDonald (1915-1983)
<snip>
> And the hip, new way of encapsulating data as well, since records can
> now also have methods, a constructor and even operators. They are
> ideal for types that are to be treated as value types. <g>
Could you explain the advantage of using a record in this way over a
normal class?
If a record has a constructor and methods it looks very much like a
normal class to me.
And with "operators", you mean some sort of operator overloading?
I've been explained why and where operator overloading is usefull so no
need to go there, just asking for some insight that i'm missing here..
--
> If a record has a constructor and methods it looks very much like a
> normal class to me.
It does look like a normal class but is missing key functionality like
inheritance, virtual methods, and destructors. An advantage of
advanced records is that they're allocated on the stack and are copied
on assignment.
--
-Mike (TeamB)
> Rudy Velthuis [TeamB] wrote:
>
> <snip>
>
> > And the hip, new way of encapsulating data as well, since records
> > can now also have methods, a constructor and even operators. They
> > are ideal for types that are to be treated as value types. <g>
>
> Could you explain the advantage of using a record in this way over a
> normal class?
Yes. Records don't have to be allocated, and have a lower memory
footprint. Dispose will take care of finalizing them, and you can use
the type as a value type, i.e. assignment will not just copy the
reference.
> If a record has a constructor and methods it looks very much like a
> normal class to me.
Not at all. The constructor simply allows you to set fields and use a
slightly different syntax, while the methods make using the record
simpler. "Operators" are simply methods which are aliases for certain
operators.
> And with "operators", you mean some sort of operator overloading?
Yes. This makes them ideal for modelling mathematical types, like
complex numbers, matrices, geometrical vectors, big integers, fixed
point types, etc. Records have value type semantics (i.e. they are
usually not allocated on the heap and don't have to be freed either,
and assignment copies the contents - i.e. no big lifetime hassles, i.e.
they behave more or less like integers or doubles).
--
Rudy Velthuis [TeamB]
"Sometimes when reading Goethe I have the paralyzing suspicion
that he is trying to be funny."
-- Guy Davenport
> Marco Caspers wrote:
>
> > From here on you can use TMyList.Create to create an instance of
> > your list, and use it in a way as you described.
>
> I would, in Delphi for Win32, derive from TObjectList, and in Delphi
> for .NET, from ArrayList, or use one as aggregate. Would make class
> design a little easier, and you don't have to take care of handling
> the list internals, only of casting to the appropriate types on
> output.
Why not derive from TList instead of TObjectlist? Does that have more
in place? Wish i had my Rad Studio key now.. Working on Vista here so
no installed (working) Delphi at hand to go study the help...
I agree with ArrayList for .NET side of things, but if you'd want to
single source it between the two, you'd be adding $IFDEF's all over the
place..
> Generics were actually designed to make doing the above, or what you
> do, unnecessary for each new element type of a list. <g>
I did mention Generics didn't i? Or did i think of it and didn't put it
in..... hmm.. memory failing, must go to bed...
I've seen some demos with Generics, and i must say that it seems very
handy.
--
> Rudy Velthuis [TeamB] wrote:
>
> > Marco Caspers wrote:
> >
> > > From here on you can use TMyList.Create to create an instance of
> > > your list, and use it in a way as you described.
> >
> > I would, in Delphi for Win32, derive from TObjectList, and in Delphi
> > for .NET, from ArrayList, or use one as aggregate. Would make class
> > design a little easier, and you don't have to take care of handling
> > the list internals, only of casting to the appropriate types on
> > output.
>
> Why not derive from TList instead of TObjectlist?
If you are using objects as item anyway, TObjectList is more ideal,
since it will take care of freeing the objects, if you like.
TObjectList returns items of type TObject already, so you can do an
"as" cast on output, which is IMO better than a hard cast.
ArrayList is the .NET equivalent (a bit more than that, actually) of
TObjectList.
--
Rudy Velthuis [TeamB]
"Pardon him, Theodotus; he is a barbarian, and thinks that the
customs of his tribe and island are the laws of nature."
-- George Bernard Shaw
Could you elaborate more?
When you say stack i think of last in first out push and pop like
handling. That doesn't sound very efficient, especially not if that
kind of record isn't local to the method, and if, heaven forbid, you
got a whole array of them...
--
> Mike Williams (TeamB) wrote:
>
> > Marco Caspers wrote:
> >
> > > If a record has a constructor and methods it looks very much like
> > > a normal class to me.
> >
> > It does look like a normal class but is missing key functionality
> > like inheritance, virtual methods, and destructors. An advantage of
> > advanced records is that they're allocated on the stack and are
> > copied on assignment.
>
> Could you elaborate more?
>
> When you say stack i think of last in first out push and pop like
> handling.
Uh-oh, I see there is some splaining to do. The stack is where local
variables reside. If a routine that has local variables starts, the
stack pointer (which is a CPU register) is lowered by the amount of
bytes required. This newly created room is where the local variables
are stored. It is also where the return address of the routine resides.
If a routine ends, the stack pointer is restored, the return address is
popped from the stack (this is something the CPU does on a RET
instruction) and execution is resumed at that address.
So allocating local variables is as simple as decrementing the stack
pointer by the number of the bytes you need for all of them together.
These variables will be discarded as soon as the routine returns, in a
similarly simple way.
http://en.wikipedia.org/wiki/Call_stack
--
Rudy Velthuis [TeamB]
"The object of war is not to die for your country but to make
the other bastard die for his."
-- General George Patton (1885-1945)
> When you say stack i think of last in first out push and pop like
> handling.
Mike is referring to the calling thread's stack, which is used for holding a
function's local variables, stack frames, parameters, etc. Any object that
is allocated on the stack is automatically freed by the compiler when it
goes out of scope, such as when the stack is cleared upon function exit.
Gambit
> I want to create a list of items where each item is made of some
> fields
>
Thank you guys. The Tcollection is perfect for my needs.I'll go with it.
Roberto
--
<snip>
> Uh-oh, I see there is some splaining to do. The stack is where local
> variables reside. If a routine that has local variables starts, the
> stack pointer (which is a CPU register) is lowered by the amount of
> bytes required. This newly created room is where the local variables
> are stored. It is also where the return address of the routine
> resides. If a routine ends, the stack pointer is restored, the
> return address is popped from the stack (this is something the CPU
> does on a RET instruction) and execution is resumed at that address.
Aha erlebnis here now..
Push/Pop used to push some registers as parameters for the "function"
that is called.
In the function you can still use push/pop but needed to make certain
you had an equal number of pop's versus pushes as if you didn't your
routine didn't return to the "intended" address <g>..
> So allocating local variables is as simple as decrementing the stack
> pointer by the number of the bytes you need for all of them together.
We're very lucky the compiler handles that nowadays...
> These variables will be discarded as soon as the routine returns, in a
> similarly simple way.
> http://en.wikipedia.org/wiki/Call_stack
Big Thank you!
--
Thanks Remy :).
--
> I want to create a list of items where each item is made of some
> fields
> is there a simply way to create this kind of structure ?
What about a dynamic array?
--
Marc Rohloff [TeamB]
marc -at- marc rohloff -dot- com
> On 17 Sep 2007 10:55:54 -0700, Roberto Nicchi wrote:
>
> > I want to create a list of items where each item is made of some
> > fields
>
> > is there a simply way to create this kind of structure ?
>
> What about a dynamic array?
Hehe, yeah, a dynamic array of records.
Nice one there, didn't think of that..
--
> I want to create a list of items where each item is made of some
> fields
> [...]
> is there a simply way to create this kind of structure ?
Peter Below's 'Typed list generator for Delphi' -
http://cc.codegear.com/Item/24490
--
Anders Isaksson, Sweden
BlockCAD: http://web.telia.com/~u16122508/proglego.htm
Gallery: http://web.telia.com/~u16122508/gallery/index.htm
> Roberto Nicchi wrote:
>
> > I want to create a list of items where each item is made of some
> > fields
> > [...]
> > is there a simply way to create this kind of structure ?
>
> Peter Below's 'Typed list generator for Delphi' -
> http://cc.codegear.com/Item/24490
In that case, you can also use Rossen Assenov's "templates" for Delphi:
http://dn.codegear.com/article/27603
--
Rudy Velthuis [TeamB]
"Hemingway was a jerk."
-- Harold Robbins
Why not use kbmMemTable and have a memory table? You can filter, search,
and sort the results. It can also save the results to a CSV file. Plus
display the contents like any other TDataset so you can use db controls
(grids) and run reports on the data. The standard version is still free.
http://www.components4programmers.com/products/kbmmemtable/
Sam