2

I'm starting to learn and actively use OOP in my projects. I've found that constructions like LDevices.Devices[i] look very cumbersome and make code hard to read.

So there's my question: is there any way to be able to make a shortcut for a field to access it like LDevices[i], not LDevices.Devices[i]? Devices is TObjectList<TDevice>.

Structure of my object, for reference:

TDeviceStorage = class (TObject)
    private
      DevicesByID: TDictionary<Integer,TDevice>;
    public
      Devices: TObjectList<TDevice>;
      Constructor Create;
      Destructor Destroy; override;
      procedure AddDevice(aID: Integer; aName, aShortName: String; aSubtype, aLocation: Integer; aSteamID: String);
      procedure Clear();
      function Count(): Integer;
      function DeviceByID(aID: Integer): TDevice;
      function DeviceIndex(aID: Integer): Integer;
  end;
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Alexander
  • 388
  • 1
  • 12
  • 1
    I edited your question to remove the second unrelated question. It's one question at a time here. – David Heffernan Apr 11 '21 at 21:44
  • 2
    Make Devices be a private, and declare an array property that you mark as default, which returns the i-th item of Devices. That said its odd that you have two collections holding the same objects. Why do you do that. – David Heffernan Apr 11 '21 at 21:45
  • @DavidHeffernan The answer is somewhat complicated. Short version: no strong reason. Long version: We have some legacy support issues causing some things to have two IDs. One is always consecutive: 0, 1, 2, 3, 4, etc. Other is from DB IDs and has holes in it: 1, 3, 8, 9, etc. So, if I have to get a device by second ID, I use Dictionary. Second reason is what children use more than one dictionary for search in objects, to search by other fields than second ID. And I've got confused who owns what previously. – Alexander Apr 11 '21 at 22:32

1 Answers1

3

Yes, this is exactly what the default directive on an array property is used for:

TDeviceStorage = class(TObject)
private
  FDevicesByID: TDictionary<Integer, TDevice>;
  FDevices: TObjectList<TDevice>;
  function GetDeviceCount: Integer;
  function GetDevice(Index: Integer): TDevice;
  procedure SetDevice(Index: Integer; const Value: TDevice);
public
  constructor Create;
  destructor Destroy; override;
  procedure AddDevice(AID: Integer; const AName, AShortName: string;
    ASubtype, ALocation: Integer; const ASteamID: string);
  procedure Clear;
  function DeviceByID(AID: Integer): TDevice;
  function DeviceIndex(AID: Integer): Integer;
  property Devices[Index: Integer]: TDevice read GetDevice write SetDevice; default;
  property DeviceCount: Integer read GetDeviceCount;
end;

with

function TDeviceStorage.GetDevice(Index: Integer): TDevice;
begin
  Result := FDevices[Index];
end;

function TDeviceStorage.GetDeviceCount: Integer;
begin
  Result := FDevices.Count;
end;

procedure TDeviceStorage.SetDevice(Index: Integer; const Value: TDevice);
begin
  FDevices[Index] := Value;
end;

Now, if DS: TDeviceStorage, you can write DS[i] to access the ith device in FDevices.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • Thanks! Great explanation. +1 – Alexander Apr 11 '21 at 21:53
  • 4
    Also, consider implementing an Enumerator for your class, then you can enumerate it in [`for..in`](http://docwiki.embarcadero.com/RADStudio/en/Declarations_and_Statements_(Delphi)#Iteration_Over_Containers_Using_For_Statements) loops without using indexes at all. – Remy Lebeau Apr 11 '21 at 22:50