Home > Net >  How to define a default indexed property for a TObject class in Delphi
How to define a default indexed property for a TObject class in Delphi

Time:12-08

Given these classes;

type TMyItem = class(TObject)
private
  FReference: String;
  FOtherProperty: TObject;
public
  property Reference: String read FReference write FReference;
  property OtherProperty: String read FOtherPropertywrite FOtherProperty;
end;

type TMyListClass = class(TObjectList<TMyItem>)
public
  function IndexOf(const AReference: String): Integer; overload;
end;

function TMyListClass.IndexOf(const AReference: String): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Count - 1 do
    if Items[I].Reference = AReference then
    begin
      Result := I;
      break;
    end;
end;

type TMyClass = class(TObject)
private
  FList: TObjectList<TOtherClass>;
public
  property List: TObjectList<TOtherClass> read FList write FList;
end;

How do I implement a property/function/enumerator on TMyClass so that instead of this

AMyClass.List.Items[AMyClass.List.IndexOf(ARef)].OtherProperty := AOtherObject;

I can do this

AMyClass[ARef].OtherProperty := AOtherObject;

I thought it would be a matter of making a default property, but you can't pass a parameter to a property like you would a function.

EDIT 07/12.

OK. So if I make List the default;, I believe this would work.

AMyClass[AMyClass.IndexOf(ARef)].OtherProperty := AOtherObject;

CodePudding user response:

The default keyword usage you are thinking of only works on array properties, but your List property is not an array property.

To get the kind of syntax you originally asked for:

AMyClass[ARef].OtherProperty := ...; 

You will have to do the following:

type
  TMyClass = class(TObject)
  private
    FList: TObjectList<TMyItem>;
    function GetItem(const AReference: String): TMyItem;
    procedure SetItem(const AReference: String; AItem: TMyItem);
  public
    property List: TObjectList<TMyItem> read FList;
    property Item[const AReference: String]: TMyItem read GetItem write SetItem; default;
  end; 

...

function TMyClass.GetItem(const AReference: String): TMyItem;
var
  Index: Integer;
begin
  Index := FList.IndexOf(AReference);
  if Index = -1 then
    raise EArgumentException.Create('Reference not found');
  Result := FList[Index];
end;

procedure TMyClass.SetItem(const AReference: String; AItem: TMyItem);
var
  Index: Integer;
begin
  Index := FList.IndexOf(AReference);
  if Index = -1 then
    FList.Add(AItem)
  else
    FList[Index] := AItem;
end;

CodePudding user response:

Edit. Sorry, I didn't update the page before posting, therefore my answer is pretty much the same as the one of @RemyLebeau.

I'm not sure I understand you right, but you can do something like this

type TMyClass = class(TObject)
private
  FList: TMyListClass;

  function  GetOtherProperty(const ARef: String): TObject;
  procedure SetOtherProperty(const ARef: String; AValue: TObject);
public
  property List: TMyListClass read FList write FList;
  property OtherProperty[const ARef: String]: TObject read GetOtherProperty write SetOtherProperty;
end;

...

function TMyClass.GetOtherProperty(const ARef: String): TObject;
begin
  Result := ... ;// find item
  if ( Result <> nil ) then
    Result := Result.OtherProperty;
end;

procedure TMyClass.SetOtherProperty(const ARef: String; AValue: TObject);
  var item: TMyItem;

begin
  item := ... ;// find item
  if ( item <> nil ) then
    item.OtherProperty := AValue;
end;

If TMyItem.FReference is unique, you may also implement something like TDictionary<String, TMyItem> in your TMyListClass and return TMyItem directly instead of its index

TMyListClass = class(TObjectList<TMyItem>)
private
  FItemDic: TDictionary<String, TMyItem>;

  function GetMyItem(const ARef: String): TMyItem;
protected
  procedure Notify(constref AValue: TMyItem; ACollectionNotification: TCollectionNotification); override;
public
  property    ItemsByRef[const ARef: String]: TMyItem read GetMyItem;
  constructor Create();
  destructor  Destroy(); override;
end;

...

procedure TMyListClass.Notify(constref AValue: TMyItem; ACollectionNotification: TCollectionNotification);
begin
  inherited Notify(AValue, ACollectionNotification);

  if ( FItemDic = nil ) then Exit;

  case ( ACollectionNotification ) of
    cnAdded : FItemDic.AddOrSetValue( AValue.Reference, AValue );
    cnRemoved, cnExtracted: FItemDic.Remove( AValue.Reference );
  end;
end;

function TMyListClass.GetMyItem(const ARef: String): TMyItem;
begin
  FItemDic.TryGetValue( ARef, Result );
end;

constructor TMyListClass.Create();
begin
  inherited;
  FItemDic := TDictionary<String, TMyItem>.Create();
end;

destructor TMyListClass.Destroy();
begin
  FreeAndNil(FItemDic);
  inherited Destroy();
end;

The code above was written in Lazarus 2.2.2, but it should work in latest Delphi as well.

  • Related