Схема Бертрана является схемой с возобновлением, и она наиболее точно описывает разумную стратегию обработки исключительных ситуаций. Не следует думать, что эта схема не может быть реализована на С#, просто она требует понимания сути и определенной структурной организации модуля. Приведу возможную реализацию такой схемы на С#:
public void Pattern ()
{
do
{
try
{
bool Danger = false;
Success = true;
MakeJob();
Danger = CheckDanger();
if (Danger)
throw (new MyException());
MakeLastJob();
}
catch (MyException me)
{
if (count > maxcount)
throw(new MyException("Три попытки были безуспешны"));
Success = false; count++;
//корректировка ситуации
Console.WriteLine("Попытка исправить ситуацию!");
level +=1;
}
}while (!Success);
}
Приведу краткие комментарии к этой процедуре, которую можно рассматривать как некоторый образец организации обработки исключительной ситуации:
• Конструкция try-catch блоков помещается в цикл do-while (!Success), завершаемый в случае успешной работы охраняемого блока, за чем следит булева переменная Success.
• В данном образце предполагается, что в теле охраняемого блока анализируется возможность возникновения исключительной ситуации и, в случае обнаружения опасности, выбрасывается собственное исключение, класс которого задан программно. В соответствии с этим тело try-блока содержит вызов метода MakeJob, выполняющего некоторую часть работы, после чего вызывается метод CheckDanger, выясняющий, не возникла ли опасность нарушения спецификации и может ли работа быть продолжена. Если все нормально, то выполняется метод MakeLastJob, выполняющий заключительную часть работы. Управление вычислением достигает конца try-блока, он успешно завершается и, поскольку остается истинной переменная Success, значение true которой установлено в начале try-блока, то цикл while, окаймляющий охраняемый блок и его обработчиков исключений, также успешно завершается.
• Если в методе CheckDanger выясняется, что нормальное продолжение вычислений невозможно, то выбрасывается исключение класса MyException. Оно перехватывается обработчиком исключения, стоящим за try-блоком, поскольку класс MyException указан как класс формального аргумента.
• Для простоты приведен только один catch-блок. В общем случае их может быть несколько, но все они строятся по единому образцу. Предполагается, что обработчик исключения может сделать несколько попыток исправить ситуацию, после чего повторно выполняется охраняемый блок. Если же число попыток, за которым следит переменная count, превосходит максимально допустимое, то обработчик выбрасывает новое исключение, задавая дополнительную информацию и передавая тем самым обработку ошибки на следующий уровень — вызываемой программе.
• Когда число попыток еще не исчерпано, обработчик исключения переменной Success дает значение false, гарантирующее повтор выполнения try-блока, увеличивает счетчик числа попыток и пытается исправить ситуацию.
• Как видите, эта схема реализует два корректных исхода обработки исключительной ситуации — Retry и Rescue — повтору с надеждой выполнить обязательства и передачи управления вызывающей программе, чтобы она предприняла попытки исправления ситуации, когда вызванная программа не смогла с этим справиться.
Доведем этот образец до реально работающего кода, где угроза исключения зависит от значения генерируемого случайного числа, а обработчик исключения может изменять границы интервала, повышая вероятность успеха.
Определим первым делом собственный класс исключений: