Catching Exceptions at the Most Appropriate Place
Using (reader) { int lineNumber = 0; // Read first line from the text file string line = reader.ReadLine(); // Read the other lines from the text file while (line!= null) { lineNumber++; Console.WriteLine("Line {0}: {1}", lineNumber, line); line = reader.ReadLine(); } } } дирования символов и несколько схем кодирования символов, таких как UTF-8 и Windows-1251, из раздела «Схемы кодирования» главы «Системы счисления и представление данных», а также из раздела «Кодировки файлов в Visual Studio». главы "Определение классов". Теперь мы немного расширим эту концепцию и будем использовать кодировки символов для правильной работы с текстовыми файлами.
Reading a Cyrillic Content
Writing to a Text File Text files are very convenient for storing various types of information. For example, we can record the results of a program. We can use text files to make something like a journal (log) for the program – a convenient way to monitor it at runtime.
Again, as with reading a text file, we will use a similar to the Console class when writing, called StreamWriter. The StreamWriter Class
о типе ошибки, месте в программе, где произошла ошибка, а также о состоянии программы в момент ошибки. Каждое исключение в.NET содержит так называемую трассировку стека, которая дает информацию о том, где именно произошла ошибка. Это будет обсуждаться более подробно позже в этой главе
Catching Exceptions in C#
After a method throws an exception, CLR is looking for an exception handler that can process the error. To understand how this works, we will take a closer look on the concept of a call-stack. The program call-stack is a stack structure that holds information about method calls, their local variables, method parameters and the memory for value types. .NET programs start from the Main(…) method, which is the entry point of the program. Another method, let’s name it "Method 1" could be called from Main. Let "Method 1" call "Method 2" and so on until "Method N" is called. When "Method N" finishes, the program flow returns back to its calling method (in our example it would be "Method N-1"), then back to its calling method and so on. This goes on until the Main(…) method is reached. Once Main(…) finishes, the entire program exits.
В разработке программного обеспечения хорошей практикой для каждого программного компонента является определение небольшого числа конкретных исключений приложений. В этом случае компонент будет генерировать только эти конкретные исключения приложения, а не стандартные исключения.NET. Таким образом, пользователи компонента программного обеспечения будут знать, что могут ожидать от него исключения.Например, если у нас есть банковское программное обеспечение и у нас есть компонент, связанный с интересами, этот компонент будет определять (и выбрасывать) исключения, такие как InterestCalculationException и InvalidPeriodException. Компонент интереса не должен генерировать исключения, такие как FileNotFoundException, DivideByZeroException и NullReferenceException. При возникновении ошибки, которая не имеет прямого отношения к расчету процентов, соответствующая акие исключения нужно обрабатывать, а какие нет?Существует одно универсальное правило, касающееся обработки исключений:Метод должен обрабатывать только те исключения, которые он ожидает и которые он знает, как обрабатывать. Все остальные исключения должны быть оставлены вызывающему методу.Если мы следуем этому правилу и каждый метод оставляет исключения, которые он не может обработать, вызывающему методу, в конце концов мы бы достигли метода Main () (или метода запуска соответствующего потока выполнения), и если этот метод не перехватывает исключение, CLR будет отображать ошибку Каждый блок try может содержать соответствующий блок finally. Код в блоке finally всегда выполняется независимо от того, как поток программы покидает блок try. Это гарантирует, что блок finally будет выполнен, даже если выдается исключение или в блоке try выполняется оператор return.Код в блоке finally не будет выполнен, если во время выполнения блока try CLR неожиданно завершится, например, если мы остановим программу через диспетчер задач Windows.Основная форма блока finally приведена ниже:
огда мы должны использовать try-finally?Во многих приложениях нам приходится работать с внешними для наших программ ресурсами. Примерами внешних ресурсов являются файлы, сетевые подключения, графические элементы, каналы и потоки к различным аппаратным устройствам (например, к принтерам, карт-ридерам и т. Д.). Когда мы имеем дело с такими внешними ресурсами, крайне важно высвободить ресурсы как можно раньше, когда ресурс больше не нужен. Например, когда мы открываем файл для чтения его содержимого (скажем, для загрузки изображения JPG), мы должны закрыть файл сразу после того, как прочитали содержимое. Если мы оставим файл открытым, операционная система не позволит другим пользователям и приложениям выполнять определенные операции с файлом. Возможно, вы столкнулись с такой ситуацией, когда вы не можете удалить какой-либо каталог или файл, потому что он используется запущенным процессом.
The hierarchical nature of exceptions allows us to catch and handle whole groups of exceptions at one time. When using catch we are not only catching the given type of exception but the whole hierarchy of exception types that are inheritors of the declared type. catch (IOException e) { // Handle IOException and all its descendants } The example above will catch not only the IOException, but all of its descendants including FileNotFoundException Catching Exceptions at the Most Appropriate Place The ability to catch exceptions at multiple locations is extremely comfortable. It allows us to handle the exception at the most appropriate place. Let’s demonstrate this with a simple comparison with the old approach using error codes. Let’s have the following method structure Method3() { Method2(); } Method2() { Method1(); } Method1() { ReadFile(); } The method Method3() calls Method2(), which calls Method1() where ReadFile() is called. Let’s suppose that Method3() is the method interested in eventual error in the ReadFile() method. If such error occurs in ReadFile() it wouldn’t be easy to transfer the error to Method3() using the traditional approach with error codes: int Method2() { errorCode = Method1(); if (errorCode!= 0) return errorCode; else DoTheActualWork(); } int Method1() { errorCode = ReadFile(); if (errorCode!= 0) return errorCode; else DoTheActualWork(); } First in Method1() we have to analyze the error code returned by ReadFile() method and eventually pass it to Method2(). In Method2() we have to analyze the error code returned by Method1() and eventually pass it to Method3() where to handle the error itself.
How can we avoid all this? Let’s remember that that the CLR searches for exceptions back in the call stack of the methods and lets each of them to define catching and handling of the exceptions. If the method is not interested in catching some exception it is simply sent back in the stack:
void Method3() { try { Method2(); } catch (Exception e) { process the exception; } } void Method2() { Method1(); } void Method1() { ReadFile(); } Лучшие практики при использовании исключений В этом разделе мы дадим некоторые рекомендации и рекомендации по правильному использованию исключений для обработки ошибок и непредвиденных ситуаций. Это важные правила, которые следует запомнить и соблюдать. Когда полагаться на исключения? Чтобы понять, когда стоит полагаться на исключения, давайте рассмотрим следующий пример: у нас есть программа, которая открывает файл по заданному пути и имени файла. Во время записи пользователь может написать неправильное имя файла. Это скорее следует считать нормальным, а не исключительным. Мы можем подготовиться и сначала проверить, существует ли файл, прежде чем пытаться открыть его: static void ReadFile(string fileName) { if (!File.Exists(fileName)) { Console.WriteLine( "The file '{0}' does not exist.", fileName); return; } StreamReader reader = new StreamReader(fileName); using (reader) { while (!reader.EndOfStream) { string line = reader.ReadLine(); Console.WriteLine(line); } } }
Бросайте исключения на соответствующем уровне абстракции! Когда мы бросаем наши собственные исключения, мы должны помнить об абстракциях в контексте наших методов. Например, если наш метод работает с массивами, мы можем выбросить IndexOutOfRangeException или NullReferenceException, поскольку наш метод работает на низком уровне и напрямую работает с памятью и элементами массива. Но если наш метод осуществляет накопление процентов на всех счетах в банке, он не должен генерировать исключение IndexOutOfRangeException, поскольку это исключение не относится к бизнес-сфере банковского сектора. Нормальным накоплением процентов в банковском программном обеспечении было бы генерировать исключение InvalidInterestException с соответствующим сообщением об ошибке, в которое должно быть добавлено исходное исключение IndexOutOfRangeException. Давайте приведем другой пример: мы вызываем метод, который сортирует массив целых чисел и выдает исключение TransactionAbortedException. Это также неуместное исключение, так же как NullReferenceException при накоплении интересов в банковском программном обеспечении. Вот почему мы должны учитывать уровень абстракции, на котором работает наш метод, когда мы генерируем наше исключение.
Всегда выдавайте адекватное, подробное и правильное сообщение об ошибке при выдаче исключений! Пользователь вашего кода должен быть в состоянии сказать, что и где проблема, и что вызвало ее при чтении сообщения об ошибке.
Воспользуйтесь поиском по сайту: ![]() ©2015 - 2025 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|