如何在 Delphi 中定义TObject类的默认索引属性

noj0wjuj  于 2022-12-12  发布在  其他
关注(0)|答案(3)|浏览(119)

给定这些类;

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;

如何在TMyClass上实现一个属性/函数/枚举数,以便

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

我能做到的

AMyClass[ARef].OtherProperty := AOtherObject;

我认为这将是一个问题,使一个默认属性,但你不能传递一个参数给一个属性,就像你传递一个函数。
2012年7月编辑。
如果我把List变成default;,我相信这是可行的。

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

vcudknz31#

default关键字的用法你正在考虑only works on array properties,但是你的List属性不是一个数组属性。
要获得您最初要求的语法类型:

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

您必须执行以下操作:

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;
9bfwbjaz

9bfwbjaz2#

编辑。抱歉,我在发帖前没有更新页面,所以我的答案和@RemyLebeau的答案差不多。
我不确定我理解的对不对,但你可以做这样的事

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;

如果TMyItem.FReference是唯一的,您也可以在TMyListClass中实现类似于TDictionary<String, TMyItem>的内容,并直接返回TMyItem而不是其索引

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;

上面的代码是用Lazarus 2.2.2编写的,但它应该也能在最新的 Delphi 中工作。

k97glaaz

k97glaaz3#

这是一个简单的任务。你可以看看 Delphi 源代码中关于如何实现默认索引属性的内容。但是如果你想改进这个解决方案,你可以声明一些默认属性,这些属性取决于你将要传递的参数的类型。下面是一些代码:

type TMyClass = class(TObject)
  private
    FList: TObjectList<TMyItem>;
    function GetItemByRef(ARef: string): TMyItem; overload;
    function GetItemByRef(AIndex: integer): TMyItem; overload;
  public
    property MyItems[ARef :string] : TMyItem read GetItemByRef; default;
    property MyItems[AIndex :integer] : TMyItem read GetItemByRef; default;
  end;

function TMyClass.GetItemByRef(ARef: string): TMyItem;
begin
  Result := nil;
  for var xItem in FList do
    if xItem.FReference = ARef then begin
      Result := xItem;
      Break;
    end;
end;

function TMyClass.GetItemByRef(AIndex: integer): TMyItem;
begin
  Result := FList[AIndex];
end;

现在,您可以通过引用或ItemIndex以及所有默认属性获取项:

procedure DoSomething;
begin
  MyClass['SomeRefString'].OtherProperty := AOtherObject;
  MyClass[5].OtherProperty := AOtherObject;
end;

相关问题