Читаем О чём не пишут в книгах по Delphi полностью

Здесь A1 получит значение 4, A2 — 3, т.e. интуитивно ожидаемые. Тем не менее полагаться на интуицию все же не стоит: в более сложных случаях она может подвести. Дело в том, что стандарт языка Паскаль разрешает разработчикам конкретной реализации языка самим выбирать порядок вычисления операндов [5]. Поэтому, даже если вам удалось добиться желаемого порядка вычисления, в следующих версиях Delphi (или при переносе на другую платформу) программа может начать работать неправильно. Таким образом, разработчик не имеет права делать какие-то предположения о том, в каком порядке будут вычисляться операнды, а когда изменение этого порядка может повлиять на результат, код должен быть написан таким образом, чтобы исключить эту возможность. В частности, пример со сложением должен быть переписан так (листинг 3.50).

Листинг 3.50. Явное управление порядком вычисления операндов

procedure TForm1.Button1Click(Sender: TObject);

var

 A1, A2: Integer;

begin

 X := 2;

 A1 := X;

 Inc(A1, GetValueAndModifyX);

 X := 2;

 A2 := GetValueAndModifyX;

 Inc(A2, X);

 Label1.Caption := IntToStr(A1);

 Label2.Caption := IntToStr(A2);

end;

Такой код, несмотря на побочные эффекты функции GetValueAndModifyX, даст ожидаемые значения при любом порядке вычисления операндов, т.к. здесь вычисление операндов разнесено по разным операторам, а порядок выполнения операторов четко определен.

Примечание

Другие компиляторы могут использовать иной порядок вычисления операндов. Так, FreePascal вычисляет их в том порядке, в каком они встречаются в выражении, т.е. в первом примере А1 получит значение 4, А2 — 3.

<p>3.4.2. Зацикливание обработчика <emphasis>TUpDown.OnClick</emphasis> при открытии диалогового окна в обработчике</p>

Для демонстрации этого "подводного камня" нам потребуется проект, на форме которого находится компонент TUpDown со следующим обработчиком события OnClick (листинг 3.51, пример UpDownDlg на компакт-диске).

Листинг 3.51. Обработчик события OnClick компонента UpDown1

procedure TForm1.UpDown1Click(Sender: TObject; Button: TUDBtnType);

begin

 Application.MessageBox('Text', 'Caption', MB_OK);

end;

Теперь, если запустить программу и нажать на верхнюю кнопку UpDown1, откроется окно с сообщением (при нажатии на нижнюю кнопку окно не будет открываться потому, что по умолчанию у компонент TUpDown свойства Position и Min равны нулю, поэтому нажатие на нижнюю кнопку не приводит к изменению значения Position, и событие OnClick не возникает; если изменить значение свойства Min или Position, то тот же эффект будет наблюдаться и при нажатии на нижнюю кнопку). Если закрыть это окно, то щелчок мышью в любом месте формы снова приведет к срабатыванию события OnClick и открытию окна, и так до бесконечности: любой щелчок по форме в любом ее месте будет снова и снова приводить к появлению сообщения. Эффект наблюдается и в том случае, когда вместо стандартного сообщения в обработчике показывается любая другая модальная форма. Кроме того, тот же эффект будет, и если использовать события OnChanging или OnChangingEx вместо OnClick, но мы далее для определенности будем говорить только об OnClick.

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

Причина этой проблемы в том, как VCL обрабатывает сообщения, которые система помещает в очередь. При нажатии на кнопку компонента TUpDown в очередь сообщений помещаются два сообщения: WM_LBUTTONDOWN и WM_NOTIFY. Компонент TUpDown по умолчанию имеет стиль csCaptureMouse — это означает, что при обработке WM_LBUTTONDOWN VCL захватывает мышь в монопольное пользование для данного компонента.

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже