Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > Не дать удалить элемент из TObjectList


Автор: kami 8.5.2010, 00:57
Имеется наследник TObjectList с несколькими (от 1 до 50) объектами в нем. Его планируется использовать в нескольких проектах.
Один из элементов этого списка (его индекс может варьироваться) удалять/заменять нельзя под страхом смертной казни (а в процессе разработки я могу забыть про это и нарваться на грабли, посему хотелось бы сразу избежать их).

Собственно - вопрос: как бы правильно (и красиво) обыграть это?

Простое перекрытие TList.Delete(index) результата не даст (т.к. вызывается в том числе и в деструкторе TList через SetCount(0)), а есть еще:
TList.Put; 
TList.SetCount(0), в котором отвратительная строчка FCount := NewCount; 
TList.Extract, в котором сперва объект исключается из списка, а потом вызывается TList.Delete(i); 
Assign и т.п (дальше просто не стал смотреть).

Заранее спасибо за участие.

Автор: bems 12.5.2010, 04:04
Сложно так вот в общем. 
А можно ли чтобы изменялся его индекс в результате удаление предыдущих? 
Чем не подходит выриант с перекрытием деструктора?
Почему бы не использовать Assert в местах, где наличие этого объекта необходимо?

Автор: Rennigth 12.5.2010, 05:07
kami, А как тебе такой вариант, особо не тестировал но...
Код

unit Unit6;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Contnrs;

type
  TSomeObjLst = class(TObjectList)
  private
    Fobj: TObject;
  protected
    procedure Notify(Ptr: Pointer; Action: TListNotification); override;
  public
    property obj: TObject read Fobj write Fobj;
  end;

  TForm6 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private

  end;

var
  Form6: TForm6;

implementation

{$R *.dfm}

procedure TForm6.FormCreate(Sender: TObject);
var
  lSomeOL: TSomeObjLst;
  lObj: TObject;
begin
  lObj := TObject.Create;
  try
    lSomeOL := TSomeObjLst.Create;
    try
      lSomeOL.Add(lObj);
      lSomeOL.obj := lObj;
      lSomeOL.Remove(lObj);
      //тут lObj существует
    finally
      lSomeOL.Free;
    end;
  finally
    lObj.Free;
  end;
end;

{ TSomeObjLst }

procedure TSomeObjLst.Notify(Ptr: Pointer; Action: TListNotification);
begin
  if not ((Action = lnDeleted) and (Ptr = obj)) then
    inherited;
end;

end.


Автор: kami 12.5.2010, 12:52
Цитата(bems @  12.5.2010,  04:04 Найти цитируемый пост)
А можно ли чтобы изменялся его индекс в результате удаление предыдущих? 

Конечно. Индекс значения не имеет. У объектов в списке есть поле ID (работает по принципу счетчика). Вот элемент с ID=0 создается автоматически и удаляться не должен. Его положение в списке может варьироваться как угодно (более того, оно, к сожалению, должно варьироваться - в интересах и для удобства пользователя).
Цитата(bems @  12.5.2010,  04:04 Найти цитируемый пост)
Чем не подходит выриант с перекрытием деструктора?

Если честно - не понял... объект с ID=0 должен содержаться в TMyObjectList от создания списка до его (списка) уничтожения. деструктор вроде тут ни при чем - т.к. список является владельцем объектов, то он и должен уничтожать их (вызов Clear в деструкторе TObjectList. Но ведь ничто не мешает вызвать Clear вне деструктора. И благодаря действиям в этом Clear оставить вживых нужный элемент не представляется возможным, нужно только перекрывать. И таких методов, перекрытие которых требуется - куча, я их перечислил в вопросе. Мне думается, должно быть более элегантное решение). 
Цитата(bems @  12.5.2010,  04:04 Найти цитируемый пост)
Почему бы не использовать Assert в местах, где наличие этого объекта необходимо?

Это уже будет игра по последствиям. А нужны превентивные меры.
Цитата(Rennigth @  12.5.2010,  05:07 Найти цитируемый пост)
А как тебе такой вариант,

Не подходит. Нотификация вызывается после удаления из списка  (по крайней мере в D7)
Код

procedure TList.Delete(Index: Integer);
var
  Temp: Pointer;
begin
  if (Index < 0) or (Index >= FCount) then
    Error(@SListIndexError, Index);
  Temp := Items[Index];
  Dec(FCount);
  if Index < FCount then
    System.Move(FList^[Index + 1], FList^[Index],
      (FCount - Index) * SizeOf(Pointer));
  if Temp <> nil then
    Notify(Temp, lnDeleted);
end;


Хотя... ведь можно сразу после удаления добавить его снова, ведь в notify до "попадания" в TObjectList.Notify объект жив...
Это мысль. Спасибо, попробую...

Автор: Демо 12.5.2010, 12:56
Я бы написал свой класс для такого.

Если всё же хочется использовать наследника, то проще всего вынести нужный элемент в поле класса и работать с ним специальными методами.

Автор: bems 13.5.2010, 00:45
Цитата(kami @  12.5.2010,  12:52 Найти цитируемый пост)
Это уже будет игра по последствиям. А нужны превентивные меры.
Если код, использующий список под твоим контролем, то этого достаточно, чтобы ты не пропустил момент, когда накосячил. 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)