}
…
}
Вы должны понимать, что в данном примере программного кода конечным получателем CarIsDeadException является среда CLR, поскольку здесь вторичное исключение генерируется методом Main(). Поэтому вашему конечному пользователю будет представлено системное диалоговое окно с информацией об ошибке. Как правило, вторично сгенерированное и частично обработанное исключение предъявляется вызывающей стороне, которая имеет возможность обработать его более "грациозно".
Вы можете догадываться, что вполне возможно генерировать исключения и во время обработки другого исключения. Например, предположим, что вы обрабатываете CarIsDeadException в рамках конкретного блока catch и в процессе обработки пытаетесь записать след стека в файл carErrors.txt на вашем диске C.
catch(CarlsDeadException e) {
// Попытка открыть файл carErrors.txt на диске C.
FileStream fs = File.Open(@"C:\carErrors.txt", FileMode.Open);
…
}
Если указанный файл на диске C не найден, попытка вызова File.Open() даст в результате FileNotFoundException. Позже мы рассмотрим пространство имен System.IO и выясним, как перед открытием файла можно программными средствами проверить наличие файла на жестком диске (и предотвратить возможность возникновения исключения). Однако здесь, чтобы сосредоточиться на теме исключений, мы предполагаем, что исключение возникло.
Когда во время обработки одного исключения обнаруживается другое исключение, лучше всего записать новый объект исключения, как "внутреннее исключение" в рамках нового объекта того же типа, что и исходное исключение (язык сломаешь!). Причина, по которой мы должны создавать новый объект исключения, заключается в том, что единственным способом документирования внутреннего исключения оказывается использование параметров конструктора. Рассмотрите следующий фрагмент программного кода.
catch (CarIsDeadException e) {
try {
FileStream fs = File.Open(@"C:\carErrors.txt", FileMode.Open);
…
} catch(Exception e2 ) {
// Генерирование исключения, записывающего новое исключение
// и сообщение первого исключения.
throw new CarIsDeadException(e.Message, e2);
}
}
Заметьте, что в данном случав мы передали объект FileNotFoundException конструктору CarIsDeadException в виде второго параметра. Сконфигурировав этот новый объект, мы направляем его по стеку вызовов следующему вызывающему объекту, в данном случае по отношению к методу Main().
С учетом того, что после Main() "следующих вызывающих объектов" для обработки исключений нет , мы снова должны увидеть диалоговое окно с сообщением об ошибке. Во многом подобно генерированию вторичных исключений, запись внутренних исключений обычно оказывается полезной только тогда, когда вызывающий объект имеет возможность красиво обработать такое исключение. В этом случае логика catch вызывающей стороны может использовать свойство InnerException для получения подробной информации об объекте внутреннего исключения.
В рамках try/catch можно также определить необязательный блок finally. Задача блока finally – обеспечить безусловное выполнение некоторого набора операторов программного кода, независимо от наличия или отсутствия исключения (любого типа). Для примера предположим, что вы хотите всегда выключать радио автомобиля перед выходом из Main(), независимо от исключений.
static void Main(string[] args) {
…
Car myCar = new Car ('"Zippy", 20);
myCar.CrankTunes(true);
try {
// Логика ускорения автомобиля.
} catch (CarIsDeadException e) {
// Обработка CarIsDeadException.
} catch (ArgumentOutOfRangeException e) {
// Обработка ArgumentOutOfRangeException.
} catch(Exception e) {
// Обработка всех остальных исключений.
} finally {
// Это выполняется всегда. Независимо от исключений.
myCar.CrankTunes(false);
}
}
Если вы не включите в конструкцию блок finally, то радио не будет выключаться, когда обнаруживается исключение (что может быть или не быть проблемой). В более реальном сценарии, когда приходится освобождать ресурсы объектов, закрывать файлы, отключаться от баз данных и т.д., блок finally может гарантировать соответствующую "уборку".
С учетом того, что методы в .NET Framework могут генерировать любое число исключений (в зависимости от обстоятельств), логичным кажется следующий вопрос: "Как узнать, какие именно исключения могут генерироваться тем или иным методом библиотеки базовых классов?" Ответ прост: это можно выяснить в документации .NET Framework 2.0 SDK. В системе справки для каждого метода указаны и исключения, которые может генерировать данный член. В Visual Studio 2005 вам предлагается альтернативный, более быстрый вариант: чтобы увидеть список всех исключений (если таковые имеются), генерируемых данным членом библиотеки базовых классов, достаточно просто задержать указатель мыши на имени члена в окне программного кода (рис. 6.6).
Читать дальше