开发者

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

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;


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;


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜