Troubles Implementing Collections / Aggregation in Lazarus lazarus / 62-General ---------------------------------------------------------------------- speleomania · 2017-01-26 20:46 Hi everyone This would be my first post here I've done my share of coding in Pascal/Delphi 10-15 years ago but haven't touched it for the last 10 years Trying to code a little app now in Lazarus but having issues implementing the nested core structure that would be the engine for my app. Here's what I'm trying to do and need help with: At the top level I'll be having a TCity class containing a number of TBuilding classes which in turn need to be able to host a number of TRoom classes. /which may have a number of TPerson classes and so on...you get the idea/ I was thinking of using TCollection and TCollectionItem classes to derive my custom classes from even though I don't really care about the visual functionality and this point so I guess TObjectList would work as well. To complicate things a little further - I'd like once the framework is in place to be able to extend any of the classes to provide additional functionality without having to modify the original classes. For example the framework should be able to work without issues with TTownHouse objects as long as they're based off the TBuilding class or TConferenceRoom if based on TRoom. Pls note that I don't need to be able to handle multiple types at once...ie all instances will be either TRoom or TConferenceRoom but not both. This is what I have so far, which unfortunately doesn't quite work: TRoom = class(TCollectionItem) RoomNo: integer; Color: TColor; end; TRooms = class(TCollection) Name: string; end; TBuilding = class(TCollectionItem) Name: string; Rooms: TRooms; end; TBuildings = class(TCollection) Name:string; end; TCity = class(TCollection) Name: string; Buildings: TBuildings; end; I'm able to do City: TCity; City := TCity.Create; City.Buildings.Create(TBuilding); // This should be able to work with City.Buildings.Create(TTownHouse); But things brake down at the third level, while I'm trying to put some rooms in my buildings: City.Buildings[0].Rooms.Create; City.Buildings[0].Room[0].Number := 101; Any hints/suggestions? I'm sure similar topics have come up before so if anyone can share a link to a similar topic on the net id very much appreciate that as well Cheers ---------------------------------------------------------------------- marcov · 2017-01-26 20:53 Quote from: speleomania on January 26, 2017, 09:46:17 pm City := TCity.Create; City.Buildings.Create(TBuilding); This is wrong. Note that you specify the TYPE of the class to create in the first line, but not in the second. The second construct, while legal, is for special cases. You need to create the Buildings collection and tell it it is a collection of  TBuildings Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i City.Buildi ngs:=TBuildings.Create(nil,TBuilding);building1:=TBuilding(City.Buildi ngs.add); Probably you want to put the above fragment in a method of city. Note that the current trunk contains code for a generics version of TCollection which is simpler in use. ---------------------------------------------------------------------- speleomania · 2017-01-26 22:17 Thanks for the reply! I'm afraid this is a bit over my head at this point: * Which unit do i need to include to be able to use the TGenericColletion type? * Using the TCollection FPC is not happy with this line: //  City.Buildings:=TBuildings.Create(nil,TBuilding);  wrong no of params * How do I go about using/accessing the third and subsequent levels: rooms, persons?   City := TCity.Create(TBuilding);   City.Buildings:=TBuildings.Create(TBuilding);   bld1:=TBuilding(City.Buildings.Add);   bld1.Name:='Oceanic';   bld1.Rooms := TRooms.Create(TRoom);   room1 := TRoom(bld1.Rooms.Add);   room1.RoomNo:=101;  ...end so on... I'd like to be able to do City.Buildings[0].Rooms[0].People[0].Name := 'Tom'; ---------------------------------------------------------------------- molly · 2017-01-26 22:23 You might want to have a look at this wiki page. You also might want to add a constructor and destructor for your classes TCity and TRoom that automatically creates and destroy the (sub)containers Buildings and Rooms for you. Even though the article does not show you the nested functionality that you seek, the basics are the same, you just splitted it up into two container objects. Your final main part of the code could then look something like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ////        main// var  City      : TCity;  ABuilding : TBuilding;  ARoom     : TRoom;  i         : integer;begin  City := TCity.Create;  City.Name := 'towny';   City.Buildings.Name := 'List of Downtown buildings of city with name City';   // Add a building  ABuilding := City.Buildings.Add;  ABuilding.Name := 'Oval Office';    // Add a room to this building  ARoom := ABuilding.Rooms.Add;  ARoom.RoomNo := 1;  ARoom.Color := 100;   // Add another room, this time with RoomNo and color at once  ARoom := ABuilding.Rooms.Add(2, 200);  WriteLn('just added a room with room number = ', ARoom.RoomNo, ' and color = ', ARoom.Color);    // Add Anoter building, this time with name already supplied  ABuilding := City.Buildings.Add('The Zoo');  WriteLn('just added a building with name = ', ABuilding.Name);   // Add a room for this building  ABuilding.Rooms.Add(24, 300);    // do a traversion:  WriteLn('City :', City.Name);  WriteLn('Number of Buildings : ', City.Buildings.Count);  for i := 0 to Pred(City.Buildings.Count) do  begin    WriteLn('Building ', Succ(i), ' has ', City.Buildings[i].Rooms.Count, ' rooms');  end;    City.Free;end.  PS: although perfectly valid, using plain class fields to access variables (personally) gives me te creeps. Usually those are declared as private fields and accessed (read /write or whatever floats your boat) using a (public/published) property. ---------------------------------------------------------------------- speleomania · 2017-01-26 23:21 Thanks Molly, The level of nested objects/collections however appears to be a problem as Im unable to do City.Buildings[0].Rooms[0].People[0].Name := 'Tom'; Issue 1: Not sure how to set Items to be a default property for the collections so that I don't have to type City.Buildings.Items[0]. ... Iddue 2: Strangely  City.Buildings.Items[0].ClassName returns TBuilding however I'm getting an error "identifier idents no member "Rooms"' as soon as I try to access the next level: city.Buildings.Items[0].Rooms.Items[0].ClassName ---------------------------------------------------------------------- speleomania · 2017-01-27 00:24 I think I solvd my issues from the previous post: TBuildings = class(TCollection) private   procedure setItem(Index: Integer; AValue: TBuilding);   function getItem(Index: integer): TBuilding; public Name:string; property Items[Index: Integer]: TBuilding read getItem write setItem; default; end;  procedure TBuildings.setItem(Index: Integer; AValue: TBuilding); begin   Items[Index].Assign(AValue); end; function TBuildings.getItem(Index: integer): TBuilding; begin   Result := TBuilding(inherited Items[Index]); end;  and implementing a similar set of functions for TRooms as well Now Im able to do City.Buildings[0].Rooms[0].RoomNo:=101; without problems ---------------------------------------------------------------------- molly · 2017-01-27 08:25 Quote from: speleomania on January 27, 2017, 01:24:50 amI think I solvd my issues from the previous post: I suspect that you might be right there Congratulations on having solved your own question(s)