The Unofficial Newsletter of Delphi Users - by Robert Vivrette

Место для чего-нибудь ...

By Lutz Lang - Lutz.Lang@t-online.de

Перевод Руденко Е.В.   janer@newmail.ru    апрель 2001 года

 

Да, я немного ленивый. Меня раздражает печатание одного и того же раз за разом. Сколько раз приходится набирать примерно такой код:

procedure TForm1.NotLazy;
var
  OldCursor: TCursor;
  OldCaption: TCaption;
begin
  OldCursor := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  OldCaption := Caption;
  Caption := 'Работаю..';
  try
    do something
  finally
    Screen.Cursor := OldCursor;
    Caption := OldCaption;
  end;
end;
Было бы хорошо иметь функцию для сохранения и восстановления таких , временно изменяемых, величин. Ok, попытаемся ее написать:
var
  OldCursor: TCursor;
  OldCaption: TCaption;

procedure StoreCursor(Cursor: TCursor);
begin
  OldCursor := Cursor;
end;

function RestoreCursor: TCursor;
begin
  Result := OldCursor;
end;

procedure StoreCaption(Caption: TCaption);
begin
  OldCaption := Caption;
end;

function RestoreCaption: TCaption;
begin
  Result := OldCaption;
end;

Теперь мы можем сократить нашу процедуру :
procedure TForm1.ABitLazy;
begin
  StoreCursor(Screen.Cursor);
  Screen.Cursor := crHourGlass;
  StoreCaption(Caption);
  Caption := 'Работаю..';
  try
    do something
  finally
    Screen.Cursor := RestoreCursor;
    Caption := RestoreCaption;
  end;
end;
Hmm.., можно сделать по другому :
var
  OldCursor: TCursor;
  OldCaption: TCaption;

function StoreCursor(Cursor: TCursor; NewCursor: TCursor): TCursor;
begin
  OldCursor := Cursor;
  Result := NewCursor;
end;

function RestoreCursor: TCursor;
begin
  Result := OldCursor;
end;

function StoreCaption(Caption: TCaption; NewCaption: TCaption): TCaption;
begin
  OldCaption := Caption;
  Caption := NewCaption;
end;

function RestoreCaption: TCaption;
begin
  Result := OldCaption;
end;

Ok, теперь в нашей процедуре только необходимый код :
procedure TForm1.ABitMoreLazy;
begin
  Screen.Cursor := StoreCursor(Screen.Cursor, crHourGlass);
  Caption := StoreCaption(Caption, 'Работаю..');
  try
    do something
  finally
    Screen.Cursor := RestoreCursor;
    Caption := RestoreCaption;
  end;
end;
Между прочим, невозможно использовать процедуру с переменными параметрами:
procedure StoreCaption(var Caption: TCaption; NewCaption: TCaption);
Caption и Cursor - это свойства  и на них должны быть ссылки.

Мне кажется, что мы имеем другую большую проблему. Что вы скажете , например, насчет следующего кода:

procedure TForm1.ABitConfused;
begin
  Screen.Cursor := StoreCursor(Screen.Cursor, crHourGlass);
  Caption := StoreCaption(Caption, 'Работаю..');
  try
    do something
    Caption := StoreCaption(Caption, 'Думаю..');
    try
      do nothing
    finally
      Caption := RestoreCaption;
    end
  finally
    Screen.Cursor := RestoreCursor;
    Caption := RestoreCaption;
  end;
end;
Первый заголовок потерян!

Хорошая идея для решения данной проблемы - использовать что-то похожее на стек. Вы можете помещать в него сколько хотите переменных для сохранения и , кода вам потребуется, возвращать их значения (в обратном порядке). И вот вам сюрприз: в Delphi есть объект  TStack! Вторая идея заключается в использовании переменных типа  Variant вместо переменных любого типа:

var
  Stack: TStack;

function Push(Value: Variant; NewValue: Variant): Variant;
var
  tmp: PVariant;
begin
  GetMem(tmp, SizeOf(Variant));
  tmp^ := Value;
  Stack.Push(tmp);
  Result := NewValue;
end;

function Pop: Variant;
var
  tmp: PVariant;
begin
  if Stack.Count > 0 then begin
    tmp := Stack.Pop;
    Result := tmp^;
    tmp^ := Null;
    FreeMem(tmp, SizeOf(Variant));
  end
  else
    Result := Null;
end;

Линия : 

tmp^ := Null;

необходима для устранения утечки памяти. Например, если элемент стека содержит строку,
FreeMem(tmp, SizeOf(Variant));
мы освободим только указатель на строку, а не саму строку.

Мы должны создавать наш стек-объект в разделе  initialization и уничтожать его в разделе  finalization :

initialization
  Stack := TStack.Create;
finalization
  Stack.Free;
end.
Итак , наш объект всегда готов к использованию. Давайте посмотрим, как выглядит теперь наша процедура:
procedure TForm1.Lazy;
begin
  Screen.Cursor := Push(Screen.Cursor, crHourGlass);
  Caption := Push(Caption, 'Работаю..');
  try
    do something
  finally
    Caption := Pop;
    Screen.Cursor := Pop;
  end;
end;
Всегда придерживайтесь принципа стека : последним зашел, первым вышел! А вот и более совершенная версия нашей процедуры:
procedure TForm1.ReallyLazy;
begin
  Screen.Cursor := Push(Screen.Cursor, crHourGlass);
  Caption := Push(Caption, 'Работаю..');
  try
    do something
    Caption := Push(Caption, 'Думаю..');
    try
      do nothing
    finally
      Caption := Pop;
    end
  finally
    Caption := Pop;
    Screen.Cursor := Pop;
  end;
end;
Остается только одна проблема.
  1.  Есть вероятность , что две или более форм будут использовать наш "стековый" объект одновременно.
  2.  Вы не можете быть уверены, что все  'pops' расположены в правильном порядке.
Мое решение - лист стековых объектов, каждая форма использует свой собственный стек. 

Будьте ленивы!

Lutz Lang
Ingenieurbuero Lang
Software Development
Lutz.Lang@T-Online.de
 

Возврат в Tips&Tricks