Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Список методов в TList (Delphi)


Автор: Louken 23.9.2008, 19:57
Вот простейший модуль:
Код

unit UnitListMethod;

interface

uses SysUtils, Classes;

type
  EEvent = class(Exception);

  TEvent = procedure(const s: string) of object;

procedure PushEvent(AEvent: TEvent);
procedure PopEvent;
procedure CallEvent(const s: string);

implementation

var FListEvent: TList;

procedure PushEvent(AEvent: TEvent);
begin
  if not Assigned(AEvent) then
    raise EEvent.Create(
      'PushEvent: not Assigned(AEvent)');

  if FListEvent = nil then
    FListEvent := TList.Create;
  FListEvent.Add(@AEvent);
end;

procedure PopEvent;
begin
  if (FListEvent = nil) or (FListEvent.Count <= 0) then
    raise EEvent.Create(
      'TmtXMLParser.PopEvent: ListEventEmpty');

  FListEvent.Delete(FListEvent.Count-1);
  if FListEvent.Count <= 0 then begin
    FListEvent.Free;
    FListEvent := nil;
  end;
end;

procedure CallEvent(const s: string);
var Proc: TEvent;
begin
  @Proc := FListEvent.Last;
  Proc(s);
end;

end.


Принцип таков, требуется организовать список методов по принципу LIFO, т.е. должен использоваться последний, загруженный в список, метод. Собсно проблемма в @Proc := FListEvent.Last; или другими словами, как из типа pointer преобразовать к TEvent.

Причем, такой код работает без проблем:
Код

type
  TProc = procedure(const s: string) of object;

procedure TForm1.ShowMsg(const s: string);
begin
  ShowMessage(s);
end;

procedure TForm1.Button1Click(Sender: TObject);
var Proc: TProc;
    q: pointer;
begin
  Proc:=ShowMsg;
  q:=@Proc;
  @Proc := q;
  Proc('1234');
end;


Два кода, в принципе, одинаковые, но в первом случае компилятор пишет, типа не удалось инициализировать переменную Proc.
Подскажите что делать, а то уже всю голову сломал. smile 

Автор: CodeMonkey 23.9.2008, 21:42
Ваш второй пример не работоспособен. Компилятор обычно такого не допускает, но в данном случае вы его "уломали".
Ваш пример работает только случайно, благодаря тому, что в ShowMsg вы не используете Self (т.е. саму форму).
Вы понимаете разницу между процедурой и методом ("of object"!)? Первое - это просто указатель на код (4 байта), второе - это указатель на код + указатель на объект (8 байт).
Соотвветствено, вы не можете просто запихнуть в TList свой TEvent без потерь. Сейчас у вас почти бред написан.
Взгляните также, например, на это: http://forum.sources.ru/index.php?showtopic=248820

Автор: VICTAR 23.9.2008, 21:52
Не вникал в суть вопроса, но возможно подойдет TQueue.

Автор: Louken 23.9.2008, 22:17
CodeMonkey
Цитата(CodeMonkey @  23.9.2008,  21:42 Найти цитируемый пост)
Ваш второй пример не работоспособен. Компилятор обычно такого не допускает, но в данном случае вы его "уломали".
Ваш пример работает только случайно, благодаря тому, что в ShowMsg вы не используете Self (т.е. саму форму).

Если я запишу ShowMessage(Self.Memo1.Text); вместо ShowMessage(s);
то все работает без проблем.

Как же мне создать список методов?
Это нужно для загрузки XML дерева. где для каждого элемента (тега) есть свой метод для загрузки.

Добавлено через 7 минут
Ладно, уже определился, буду использовать динамический массив типа:
var FListEvent: array of TEvent;
Всем спасибо.

Автор: CodeMonkey 24.9.2008, 05:24
Цитата(Louken @  23.9.2008,  22:17 Найти цитируемый пост)
Если я запишу ShowMessage(Self.Memo1.Text); вместо ShowMessage(s);то все работает без проблем.

Потому что в Proc у вас как сидел объект, так и сидит - вы его не читаете и не записываете. А вот если бы вы копировали Proc, скажем, в Proc2 через q - вот тогда бы и нарвались.

А вариантов через List может быть даже несколько. Самый муторный - выделять память под каждый элемент списка. Попроще - хранить в нечётных элементах TList указатели на код, в чётных - указатели на объект. В таком разрезании на части вам поможет тип TMethod - посмотрите его определение. К нему можно привести TEvent. Ну и ещё всяко разно можо придумать всего.

P.S. Эх, были бы раньше в Delphi генерики, ничего делать бы и не пришлось. Объявил бы TList<TEvent> и... всё.

Автор: Louken 24.9.2008, 16:24
CodeMonkey
Теперь то нормально?
Код

type
  EXMLEvent = class(Exception);
  
  PmtXMLEvent = ^TmtXMLEvent;
  TmtXMLEvent = procedure(const s: string) of object;
    
  TmtXMLParser = class
  private
    FListXMLEvent: TList;
    
    procedure CallXMLEvent(const s: string);
    procedure ClearXMLEvent;
  public
    destructor Destroy; override;
    procedure PushXMLEvent(AXMLEvent: TmtXMLEvent);
    procedure PopXMLEvent;
  end;
  
implementation

destructor TmtXMLParser.Destroy;
begin
  ClearXMLEvent;
  inherited Destroy;
end;

procedure TmtXMLParser.PushXMLEvent(AXMLEvent: TmtXMLEvent);
var Event: PmtXMLEvent;
begin
  if not Assigned(AXMLEvent) then
    raise EXMLEvent.Create(
      'TmtXMLParser.PushXMLEvent: not Assigned(AXMLEvent)');

  if FListXMLEvent = nil then
    FListXMLEvent := TList.Create;

  New(Event);
  Event^ := AXMLEvent;

  FListXMLEvent.Add(Event);
end;

procedure TmtXMLParser.PopXMLEvent;
begin
  if (FListXMLEvent = nil) or (FListXMLEvent.Count <= 0) then
    raise EXMLEvent.Create(
      'TmtXMLParser.PopEvent: ListEventEmpty');

  Dispose(FListXMLEvent.Last);

  FListXMLEvent.Delete(FListXMLEvent.Count-1);
  if FListXMLEvent.Count <= 0 then begin
    FListXMLEvent.Free;
    FListXMLEvent := nil;
  end;
end;

procedure TmtXMLParser.CallXMLEvent(const s: string);
var Event: PmtXMLEvent;
begin
  if (FListXMLEvent = nil) or (FListXMLEvent.Count <= 0) then
    raise EXMLEvent.Create(
      'TmtXMLParser.CallXMLEvent: ListXMLEventEmpty');

  Event := FListXMLEvent.Last;
  Event^(s);
end;

procedure TmtXMLParser.ClearXMLEvent;
var i: integer;
begin
  if FListXMLEvent = nil then Exit;
  
  for i:=0 to FListXMLEvent.Count-1 do
    Dispose(FListXMLEvent.Items[i]);
    
  FListXMLEvent.Free;
  FListXMLEvent := nil;
end;    

Автор: CodeMonkey 25.9.2008, 08:05
Ну вроде похоже на правду smile

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