Рассмотрим типичный пример:
- Код: Выделить всё
Select MsgBox(..., ..., vbYesNoCancel)
Case vbYes
SaveAllFiles
Case vbCancel
Cancel = True
Exit Sub
End Select
TerminateApplication
End Select
Думаю понятно, что в рамках этого примера функция SaveAllSettings сохраняет изменения в файлах, а TerminateApplication - окончательно завершает приложение.
И кажется вполне логичным, что обработчик vbNo не реализован. В нём был бы только TerminateApplication, но тогда и в vbYes надо было бы ставить TerminateApplication. А здесь программист вынес TerminateApplication за пределы Select'а, а обработчик случая vbNo просто не создал (потому как после вынесения из него вызова TerminateApplication, там попросту ничего бы не осталось).
Это красивый код? Нет. Отстойный код.
Дело в том, что хоть это и ужасно маловероятно, но всё-таки возможно (особенно при нехватке памяти -- такое происходит постоянно), что вызов MsgBox'а обломится где-то внутри (конкретно - где-то внутри MessageBox'а), и никакого окна сообщения не появится, и функция сразу же вернёт нам 0. (Поверьте, при нехватке памяти, скорее всего, никакой ошибки Out of memory не будет: MessageBox вернёт MsgBox'у ноль и MsgBox послушно нам этот 0 возвратит).
Теперь давайте посмотрим, как поведёт наш код себя в такой ситуации:
Вызывается MsgBox, но из-за сбоя, сообщение не создаётся, юзер ничего не замечает, сразу же возвращается 0.
0 сравнивается с vbYes. Т.к. 0 не равен vbYes первый Case не срабатывает.
0 сравнивается с vbCancel. Т.к. 0 так же не равен vbCancel, второй Case тоже не срабатывает.
SelectCase заканчивается. Вызывается функция TerminateApplication - приложение тихо закрывается.
А теперь ещё представим, как это будет смотреться со стороны пользователя:
Чел наоткрывал кучу приложений, таким образом, что возникла катастрофическая нехватка памяти. Чел открыл блокнот и написал в нём довольно важный текст (номер счёта, к примеру, который ему продиктовали по телефону). Чел лезет мышкой нажать на крестик, ожидая, что появится привычный вопрос, что он нажмёт "Да", сохранит файл, после чего блокнот автоматом закроется. Но как только чел жмёт на крестик - блокнот с шумом (вызвать MessageBeep памяти всё же должно хватить) закрывается. Без всяких вопросов. Вместе с номером банковского счёта.
Действительно, неприятная ситуация?
Поэтому, вывод: Всегда пишите так, чтобы сбои внешних функций нанесли минимальный ущерб вашей программе и, главное, данным, с которыми она работает (они зачастую намного ценнее, чем программа).
В данном случае, наиболее безопасным вариантом был бы следующий:
- Код: Выделить всё
Select MsgBox(..., ..., vbYesNoCancel)
Case vbYes
SaveAllFiles
Case vbNo
Case Else
Cancel = True
Exit Sub
End Select
TerminateApplication
End Select
________________
Я могу с уверенностью сказать, что этот подход используется в продуктах MS. Поэтому с Блокнотом, в действительности, ничего страшного не произойдёт. В то время как многие программы (особенно это касается ненавидимого мною Svoi.NET PHP Edit'а ) если им не хватит чего-нибудь (памяти там, или совести) отобразить диалог с вопросом - тупо закрываются.
Для всякого рода запросов к пользователю этот принцип можно сформулировать так:
Если вы задаёте вопрос пользователю на тему совершить ли какое-либо опасное действие или не совершать, следует проверять ответ "Да" и совершать только в этом случае и не совершать во остальных, а не наоборот.
Т.е. если речь идёт о вопросе "Удалить выбранный файл", надо делать так:
- Код: Выделить всё
If PromptForDelete() = Yes Then
Kill
Else
DontKill
End if
а ни в коем случае не так:
- Код: Выделить всё
If PromptForDelete() = No Then
DontKill
Else
Kill
End if
По этой же самой причине, создавая константы, подобные тем, что содержатся в энуме vbMsgBoxResult, надо всегда стараться давать значение 0 нейтральному элементу. Чтобы факт возврата функцией нуля не был случайно воспринят как согласие на какое-то потенциально-опасное необратимое действие.