Try catch c как поймать любое исключение
Перейти к содержимому

Try catch c как поймать любое исключение

  • автор:

Перехват исключений в Visual C++

В этой статье описывается try-catch-finally использование блока для перехвата исключения.

Исходная версия продукта: Visual C++
Исходный номер базы знаний: 815662

Сводка

Блок try-catch-finally — это оболочка, которая помещается вокруг любого кода, в котором может возникнуть исключение. Перехват и работа с исключениями — это стандартные задачи программирования.

Блок try-catch-finally состоит из следующих разделов:

  • Любой код, который может вызвать исключение, помещается в блок try.
  • Если возникает исключение, catch вводится блок, и программа может выполнить соответствующую операцию для восстановления или оповещения пользователя.
  • Код в блоке finally всегда выполняется и может выполнять очистку после возникновения исключения. Блок finally необязателен.

В этой статье рассматриваются следующие пространства имен библиотеки классов Microsoft платформа .NET Framework: System.IO и System.Security .

Перехват исключений в Visual C++ .NET

  1. Запустите Visual Studio .NET.
  2. В меню Файл выберите пункт Создать и затем пункт Проект.
  3. В Visual C++ щелкните Visual C++ в разделе Типы проектов, а затем выберите консольное приложение CLR в разделе Шаблоны.
  4. В поле Имя введите Q815662 и нажмите кнопку ОК.
  5. Замените весь код в окне кода Q815662.cpp следующим кодом. Код объявляет и инициализирует три переменные. Инициализация k вызывает ошибку.
#include "stdafx.h" #using #include using namespace System; void _tmain(void) < Console::WriteLine("We're going to divide 10 by 0 and see what happens. "); Console::WriteLine(); int i = 10; int j = 0; int k = i/j; //Error on this line. >
#include "stdafx.h" #using #include using namespace System; void _tmain(void) < try < Console::WriteLine("We're going to divide 10 by 0 and see what happens. "); Console::WriteLine(); int i = 10; int j = 0; int k = i/j; //Error on this line. >catch(. ) < Console::WriteLine("An error occurred."); >> 

Примечание. Сообщение об ошибке catch из блока отображается вместо сообщения об ошибке системного исключения.

This statement is always printed. 

Замените код в окне кода Q815662.cpp следующим кодом:

#include "stdafx.h" #using #include using namespace System; void _tmain(void) < try < Console::WriteLine("We're going to divide 10 by 0 and see what happens. "); Console::WriteLine(); int i = 10; int j = 0; int k = i/j; //Error on this line. >catch(. ) < Console::WriteLine("An error occurred."); >__finally //This section is performed regardless of the above processing. < Console::WriteLine(); Console::WriteLine("This statement is always printed"); >> 
#include "stdafx.h" #using #include using namespace System; using namespace System::Reflection; void _tmain(void) < try < Console::WriteLine("We're going to divide 10 by 0 and see what happens. "); Console::WriteLine(); int i = 10; int j = 0; int k = i/j; //Error on this line. >catch(Exception *e) < Console::WriteLine("An error occurred."); Console::WriteLine(e->Message); // Print the error message. Console::WriteLine(e->StackTrace); //String that contains the stack trace for this exception. > __finally //This section is performed regardless of the above processing. < Console::WriteLine(); Console::WriteLine("This statement is always printed"); >Console::ReadLine(); > 
#include "stdafx.h" #using #include using namespace System; using namespace System::IO; using namespace System::Security; void _tmain(void) < try < File::Create("c:\\temp\\testapp.txt"); //Can fail for a number of resons >// This error may occur if the temp folder does not exist. catch(IOException *ioe) < Console::WriteLine("An IOException exception occurred!"); Console::WriteLine(ioe->ToString()); > // You do not have the appropriate permission to take this action. catch(SecurityException *se) < Console::WriteLine("An SecurityException exception occur") >// Catch all exceptions catch(Exception *e) < Console::WriteLine(e->ToString()); > > 

Обратная связь

Были ли сведения на этой странице полезными?

Исключения

В процессе работы программы могут возникать различные ошибки. Например, при передаче файла по сети оборвется сетевое подключение или будут введены некорректные и недопустимые данные, которые вызовут падение программы. Такие ошибки еще называются исключениями. Исключение представлякт временный объект любого типа, который используется для сигнализации об ошибке. Цель объекта-исключения состоит в том, чтобы передать информацию из точки, в которой произошла ошибка, в код, который должен ее обработать. Если исключение не обработано, то при его возникновении программа прекращает свою работу.

Например, в следующей программе происходит деление чисел:

#include double divide(int a, int b) < return a / b; >int main() < int x; int y<>; double z ; std::cout

Эта программа успешно скомпилируется, но при ее выполнении возникнет ошибка, поскольку в коде производится деление на ноль, после чего программа аварийно завершится.

С одной стороны, мы можем в функции divide определить проверку и выполнять деление, если параметр b не равен 0. Однако нам в любом случае надо возвращать из функции divide некоторый результат — некоторое число. То есть мы не можем просто написать:

double divide(int a, int b) < if (b) return a / b; else std::cout

И в этом случае нам надо известить систему о возникшей ошибке. Для этого используется оператор throw .

Оператор throw генерирует исключение. Через оператор throw можно передать информацию об ошибке. Например, функция divide могла бы выглядеть следующим образом:

double divide(int a, int b) < if (b) return a / b; throw "Division by zero!"; >

То есть если параметр b равен 0, то генерируем исключение.

Но это исключение еще надо обработать в коде, где будет вызываться функция divide. Для обработки исключений применяется конструкция try. catch . Она имеет следующую форму:

try < инструкции, которые могут вызвать исключение >catch(объявление_исключения)

В блок кода после ключевого слова try помещается код, который потенциально может сгенерировать исключение.

После ключевого слова catch в скобках идет параметр, который передает информацию об исключении. Затем в блоке производится собственно обработка исключения.

Так изменим весь код следующим образом:

#include double divide(int a, int b) < if (b) return a / b; throw "Division by zero!"; >int main() < int x; int y<>; try < double z ; std::cout catch (. ) < std::cout std::cout

Код, который потенциально может сгенерировать исключение — вызов функции divide помещается в блок try.

В блоке catch идет обработка исключения. Причем многоточие в скобках после оператора catch ( catch(. ) ) позволяет обработать любое исключение.

В итоге когда выполнение программы дойдет до строки

double z ;

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

Error! The End.

Однако в данном случае мы только знаем, что произошла какая-то ошибка, а какая именно, неизвестно. Поэтому через параметр в блоке catch мы можем получить то сообщение, которое передается оператору throw :

#include >iostream < double divide(int a, int b) < if (b) return a / b; throw "Division by zero!"; >int main() < int x; int y<>; try < double z ; std::cout catch (const char* error_message) < std::cout std::cout

С помощью параметра const char* error_message получаем сообщение, которое предано оператору throw, и выводим это сообщение на консоль. Почему здесь мы получаем сообщение об ошибке в виде типа const char* ? Потому что после оператора throw идет строковый литерал, который представляет как раз тип const char* . И в этом случае консольный вывод будет выглядеть следующим образом:

Division by zero! The End.

Таким образом, мы можем узнать суть возникшего исключения. Подобным образом мы можем передавать информацию об исключении через любые типы, например, std::string:

throw std::string;

Тогда в блоке catch мы можем получить эту информацию в виде объекта std::string :

catch (std::string error_message)

Если же исключение не обработано, то вызывается функция std::terminate() (из модуля стандартной библиотеки C++), которая, в свою очередь, по умолчанию вызывает другую функцию — std::abort() (из ), которая собственно и завершает программу.

Существует очень много функций и в стандартной библиотеке С++, и в каких-то сторонних библиотеках. И может возникнуть вопрос, какие из них вызывать в конструкции try-catch, чтобы не столкнуться с необработанным исключением и аварийным завершением программы. В этом случае может помочь прежде всего документация по функции (при ее наличии). Другой сигнал — ключевое слово noexcept , которое при использовании в заголовке функции указывает, что эта функция никогда не будет генерировать исключения. Например:

void print(int argument) noexcept;

Здесь указываем, что функция print() никогда не вызовет исключение. Таким образом, встретив функцию с подобным ключевым словом, можно ожидать, что она не вызовет исключения. И соответственно нет необходимости помещать ее вызов в конструкцию try-catch.

Создание объекта исключения

При обработке исключения стоит помнить, что при передаче объекта оператору throw блок catch получает копию этого объекта. И эта копия существует только в пределах блока catch.

Для значений примитивных типов, например, int , копирование значения может не влиять на производительность программы. Однако при передаче объектов классов издержки могут выше. Поэтому в этом случае объекты обычно передаются по ссылке, например:

#include double divide(int a, int b) < if (b) return a / b; throw std::string; > int main() < int x; int y<>; try < double z ; std::cout catch (const std::string& error_message) // строка передается по ссылке < std::cout std::cout

Обработка и генерация разных типов исключений

Мы можем генерировать и обрабатывать несколько разных исключительных ситуаций. Допустим, нам надо, чтобы при делении делитель (второе число) был не больше, чем делимое (первое число):

#include double divide(int a, int b) < if(!b) // если b == 0 < throw 0; >if(b > a) < throw "The second number is greater than the first one"; >return a / b; > void test(int a, int b) < try < double result ; std::cout catch (int code) < std::cout catch (const char* error_message) < std::cout > int main() < test(100, 20); // 5 test(100, 0); // Error code: 0 test(100, 1000); // The second number is greater than the first one >

В функции divide в зависимости от значения числа b оператору throw передаем либо число:

throw 0;

либо строковый литерал:

throw "The second number is greater than the first one";

Для тестирования функции divide определена другая функция — test, где вызов функции divide() помещен в конструкцию try..catch . Поскольку при генерации исключения мы можем получать ошибку в виде двух типов — int (если b равно 0) и const char* (если b больше a), то для обработки каждого типа исключений определены два разных блока catch:

catch (int code) < std::cout catch (const char* error_message)

В функции main вызываем функцию test, передавая в нее различные числа. При вызове:

test(100, 20); // 5

число b не равно 0 и меньше a, поэтому никаких исключений не возникает, блок try срабатывает до конца, и функция завершает свое выполнение.

При втором вызове

test(100, 0); // Error code: 0

число b равно 0, поэтому генерируется исключение, а в качестве объекта исключения передается число 0. Поэтому при возникновении исключения программа выберет тот блок catch, где обрабатывается исключения типа int:

catch (int code)

При третьем вызове

test(100, 1000); // The second number is greater than the first one

число b больше a, поэтому объект исключения будет представлять строковый литерал или const char* . Поэтому при возникновении исключения программа выберет блок catch, где обрабатывается исключения типа const char*:

catch (const char* error_message)

Таким образом, в данном случае мы получим следующий консольный вывод:

5 Error code: 0 The second number is greater than the first one

Может быть ситуация, когда генерируется исключение внутри конструкции try-catch , и даже есть блок catch для обработки исключений, однако он обрабатывает другие типы исключений:

void test(int a, int b) < try < double result ; std::cout catch (const char* error_message) < std::cout >

Здесь нет блока catch для обработки исключения типа int . Поэтому при генерации исключения:

throw 0;

Программа не найдет нужный блок catch для обработки исключения, и программа аварийно завершит свое выполнение.

try-catch и деструкторы

Стоит отметить, что, если в блоке try создаются некоторые объекты, то при возникновении исключения у них вызываются деструкторы. Например:

#include class Person < public: Person(std::string name) :name< name > < std::cout ~Person() < std::cout void print() < throw "Print Error"; >private: std::string name; >; int main() < try < Person tom< "Tom" >; tom.print(); // Здесь генерируется ошибка > catch (const char* error) < std::cerr >

В классе Person определяет деструктор, который выводит сообщение на консоль. В функции print просто генерируем исключение.

В функции main в блоке try создаем один объект Person и вызываем у него функцию print, что естественно приведет к генерарции исключения и переходу управления программы в блок catch . И если мы посмотрим на консольный вывод

Person Tom created Person Tom deleted Print Error

то мы увидим, что прежде чем начнется обработка исключения в блоке catch, будет вызван деструктор объекта Person.

Лучшие методики обработки исключений

Хорошо спроектированное приложение обрабатывает исключения и ошибки, чтобы предотвратить сбои приложения. В этой статье описаны рекомендации по обработке и созданию исключений.

Использование блоков try/catch/finally для восстановления после ошибок или высвобождения ресурсов

Используйте try / catch блоки вокруг кода, который может создать исключение, и код может восстановиться после этого исключения. В блоках catch следует всегда упорядочивать исключения от более производных к менее производным. Все исключения являются производными от Exception класса. Более производные исключения не обрабатываются предложением catch, предшествующим предложению catch для базового класса исключений. Если код не может восстановиться из исключения, не перехватывайте это исключение. Включите методы выше по стеку вызовов для восстановления по мере возможности.

Очистка ресурсов, выделенных с помощью using инструкций или finally блоков. Рекомендуется использовать инструкции using для автоматической очистки ресурсов при возникновении исключений. Используйте блоки finally , чтобы очистить ресурсы, которые не реализуют IDisposable. Код в предложении finally выполняется почти всегда — даже при возникновении исключений.

Обработка общих условий без выдачи исключений

Для условий, которые могут возникнуть, но способны вызвать исключение, рекомендуется реализовать обработку таким способом, который позволит избежать исключения. Например, если вы попытаетесь закрыть подключение, которое уже закрыто, вы получите InvalidOperationException . Этого можно избежать, используя оператор if для проверки состояния подключения перед попыткой закрыть его.

if (conn->State != ConnectionState::Closed) < conn->Close(); > 
if (conn.State != ConnectionState.Closed)
If conn.State <> ConnectionState.Closed Then conn.Close() End IF 

Если вы не проверка состояние подключения перед закрытием InvalidOperationException , можно поймать исключение.

try < conn->Close(); > catch (InvalidOperationException^ ex) < Console::WriteLine(ex->GetType()->FullName); Console::WriteLine(ex->Message); > 
try < conn.Close(); >catch (InvalidOperationException ex)
Try conn.Close() Catch ex As InvalidOperationException Console.WriteLine(ex.GetType().FullName) Console.WriteLine(ex.Message) End Try 

Выбор конкретного способа зависит от того, насколько часто ожидается возникновение данного события.

  • Используйте обработку исключений, если событие не происходит часто, то есть если событие действительно исключительное и указывает на ошибку, например непредвиденный конец файла. При использовании обработки исключений в обычных условиях выполняется меньше кода.
  • Если событие происходит регулярно в рамках нормальной работы программы, выполняйте проверку на наличие ошибок прямо в коде. Проверка на наличие распространенных условий ошибки позволяет выполнять меньший объем кода благодаря устранению исключений.

Устранение исключений при разработке классов

Класс может предоставлять методы и свойства, позволяющие избежать вызова, способного выдать исключение. Например, класс FileStream содержит методы, позволяющие определить, достигнут ли конец файла. Эти методы можно использовать для предотвращения исключения, которое возникает при чтении в конце файла. В следующем примере показано, как считывать конец файла без активации исключения:

class FileRead < public: void ReadAll(FileStream^ fileToRead) < // This if statement is optional // as it is very unlikely that // the stream would ever be null. if (fileToRead == nullptr) < throw gcnew System::ArgumentNullException(); >int b; // Set the stream position to the beginning of the file. fileToRead->Seek(0, SeekOrigin::Begin); // Read each byte to the end of the file. for (int i = 0; i < fileToRead->Length; i++) < b = fileToRead->ReadByte(); Console::Write(b.ToString()); // Or do something else with the byte. > > >; 
class FileRead < public void ReadAll(FileStream fileToRead) < // This if statement is optional // as it is very unlikely that // the stream would ever be null. if (fileToRead == null) < throw new ArgumentNullException(); >int b; // Set the stream position to the beginning of the file. fileToRead.Seek(0, SeekOrigin.Begin); // Read each byte to the end of the file. for (int i = 0; i < fileToRead.Length; i++) < b = fileToRead.ReadByte(); Console.Write(b.ToString()); // Or do something else with the byte. >> > 
Class FileRead Public Sub ReadAll(fileToRead As FileStream) ' This if statement is optional ' as it is very unlikely that ' the stream would ever be null. If fileToRead Is Nothing Then Throw New System.ArgumentNullException() End If Dim b As Integer ' Set the stream position to the beginning of the file. fileToRead.Seek(0, SeekOrigin.Begin) ' Read each byte to the end of the file. For i As Integer = 0 To fileToRead.Length - 1 b = fileToRead.ReadByte() Console.Write(b.ToString()) ' Or do something else with the byte. Next i End Sub End Class 

Другим способом избежать исключений является возврат null (или по умолчанию) для большинства распространенных случаев ошибок вместо того, чтобы вызвать исключение. Распространенный случай ошибки можно рассматривать как обычный поток управления. Возвращая значение NULL (или значение по умолчанию) в таких случаях, можно уменьшить влияние на производительность приложения.

Для типов значений, следует ли использовать Nullable или по умолчанию в качестве индикатора ошибки, что следует учитывать для приложения. При использовании Nullable default принимает значение null , а не Guid.Empty . Иногда добавление Nullable может сделать его более понятным, если значение присутствует или отсутствует. В других случаях добавление Nullable может создавать дополнительные случаи для проверка, которые не нужны и служат только для создания потенциальных источников ошибок.

Выдача исключений вместо возврата кода ошибки

Исключения гарантируют, что сбои не замечены, так как вызывающий код не проверка возвращаемого кода.

Использование предопределенных типов исключений .NET

Создавайте новый класс исключений, только если предопределенное исключение не подходит. Например:

  • Если вызов набора свойств или метода не подходит, учитывая текущее состояние объекта, создайте InvalidOperationException исключение.
  • Если передаются недопустимые параметры, создайте ArgumentException исключение или один из предопределенных классов, производных от ArgumentException.

Завершайте имена классов исключений словом Exception

Если требуется пользовательское исключение, присвойте ему соответствующее имя и сделайте его производным от класса Exception. Например:

public ref class MyFileNotFoundException : public Exception < >; 
public class MyFileNotFoundException : Exception
Public Class MyFileNotFoundException Inherits Exception End Class 

Включение трех конструкторов в пользовательские классы исключений

При создании собственных классов исключений можно использовать по меньшей мере три общих конструктора: конструктор без параметров, конструктор, принимающий строковое сообщение, и конструктор, принимающий строковое сообщение и внутреннее исключение.

  • Exception(), использующий значения по умолчанию.
  • Exception(String), принимающий строковое сообщение.
  • Exception(String, Exception), принимающий строковое сообщение и внутреннее исключение.

Обеспечение доступности данных об исключении при удаленном выполнении кода

При создании определяемых пользователем исключений убедитесь, что метаданные исключений доступны для кода, выполняющегося удаленно.

Например, в реализациях .NET, поддерживающих домены приложений, исключения могут возникать в доменах приложений. Предположим, домен приложения A создает домен приложения B, который выполняет код, который создает исключение. Чтобы домен приложения A правильно перехватывал и обрабатывал исключение, он должен иметь возможность найти сборку, содержащую исключение, созданное доменом приложения B. Если домен приложения B создает исключение, содержащееся в сборке под его базой приложения, но не в базе приложения A домена приложения, домен приложения A не сможет найти исключение, а среда CLR вызовет FileNotFoundException исключение. Чтобы избежать этой ситуации, можно развернуть сборку, содержащую сведения об исключении двумя способами:

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

Использование грамматически правильных сообщений об ошибке

Составляйте понятные предложения, указывая в конце знаки препинания. Каждое предложение в строке, назначенной свойству Exception.Message, должно заканчиваться точкой. Например, «Таблица журнала переполнена».

Включение локализованной строки сообщения в каждое исключение

Сообщение об ошибке, которое видит пользователь, является производным от Exception.Message свойства создаваемого исключения, а не от имени класса исключений. Как правило, значение присваивается Exception.Message свойству путем передачи строки message сообщения в аргумент конструктора исключений.

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

  • Практическое руководство. Создание определяемых пользователем исключений с локализованными сообщениями об исключениях
  • Ресурсы в приложениях .NET
  • System.Resources.ResourceManager

Предоставление дополнительных свойств в пользовательских исключениях по мере необходимости

Дополнительные сведения (кроме строки настраиваемого сообщения) включайте в исключение только в случаях, когда в соответствии со сценарием программирования такие дополнительные сведения могут оказаться полезными. Например, исключение FileNotFoundException предоставляет свойство FileName.

Размещение операторов throw для удобной трассировки стека

Трассировка стека начинается в операторе, породившем исключение, и завершается оператором catch , перехватывающим это исключение.

Использование методов построителя исключений

Обычно класс создает одно и то же исключение из разных мест в его реализации. Чтобы избежать повторения кода, используйте вспомогательные методы, создающие исключение и затем возвращающие его. Например:

ref class FileReader < private: String^ fileName; public: FileReader(String^ path) < fileName = path; >array^ Read(int bytes) < array^ results = FileUtils::ReadFromFile(fileName, bytes); if (results == nullptr) < throw NewFileIOException(); >return results; > FileReaderException^ NewFileIOException() < String^ description = "My NewFileIOException Description"; return gcnew FileReaderException(description); >>; 
class FileReader < private string fileName; public FileReader(string path) < fileName = path; >public byte[] Read(int bytes) < byte[] results = FileUtils.ReadFromFile(fileName, bytes); if (results == null) < throw NewFileIOException(); >return results; > FileReaderException NewFileIOException() < string description = "My NewFileIOException Description"; return new FileReaderException(description); >> 
Class FileReader Private fileName As String Public Sub New(path As String) fileName = path End Sub Public Function Read(bytes As Integer) As Byte() Dim results() As Byte = FileUtils.ReadFromFile(fileName, bytes) If results Is Nothing Throw NewFileIOException() End If Return results End Function Function NewFileIOException() As FileReaderException Dim description As String = "My NewFileIOException Description" Return New FileReaderException(description) End Function End Class 

В некоторых случаях для создания исключения лучше воспользоваться конструктором исключений. В качестве примера можно привести класс глобальных исключений, например ArgumentException.

Восстановление состояния, если методы не выполняются из-за исключения

Вызывающие объекты должны предполагать, что при создании исключения из метода не возникают побочные эффекты. Например, если у вас есть код, который передает деньги, списывая их с одного счета и внося на другой, и при начислении средств возникает исключение, списание средств применяться не должно.

public void TransferFunds(Account from, Account to, decimal amount) < from.Withdrawal(amount); // If the deposit fails, the withdrawal shouldn't remain in effect. to.Deposit(amount); >
Public Sub TransferFunds(from As Account, [to] As Account, amount As Decimal) from.Withdrawal(amount) ' If the deposit fails, the withdrawal shouldn't remain in effect. [to].Deposit(amount) End Sub 

Предыдущий метод не создает никаких исключений напрямую. Однако необходимо написать метод, чтобы отмена вывода была отменена, если операция депозита завершается сбоем.

Один из способов обработки в этой ситуации заключается в перехвате всех исключений, выданных транзакцией начисления средств, и откате транзакции списания средств.

private static void TransferFunds(Account from, Account to, decimal amount) < string withdrawalTrxID = from.Withdrawal(amount); try < to.Deposit(amount); >catch < from.RollbackTransaction(withdrawalTrxID); throw; >> 
Private Shared Sub TransferFunds(from As Account, [to] As Account, amount As Decimal) Dim withdrawalTrxID As String = from.Withdrawal(amount) Try [to].Deposit(amount) Catch from.RollbackTransaction(withdrawalTrxID) Throw End Try End Sub 

В этом примере показано использование throw повторного изменения исходного исключения, что упрощает для вызывающих абонентов видеть реальную причину проблемы без необходимости проверки InnerException свойства. Альтернативой является создание нового исключения и включение исходного исключения в качестве внутреннего исключения.

catch (Exception ex) < from.RollbackTransaction(withdrawalTrxID); throw new TransferFundsException("Withdrawal failed.", innerException: ex) < From = from, To = to, Amount = amount >; > 
Catch ex As Exception from.RollbackTransaction(withdrawalTrxID) Throw New TransferFundsException("Withdrawal failed.", innerException:=ex) With < .From = from, .[To] = [to], .Amount = amount >End Try 

Запись исключений для повторного восстановления позже

Чтобы записать исключение и сохранить его вызовы, чтобы иметь возможность повторно выполнить его повторно, используйте System.Runtime.ExceptionServices.ExceptionDispatchInfo класс. Этот класс предоставляет следующие методы и свойства (среди прочего):

  • Используется ExceptionDispatchInfo.Capture(Exception) для записи исключения и стека вызовов.
  • Используется ExceptionDispatchInfo.Throw() для восстановления состояния, сохраненного при захвате исключения и повторном выполнении записанного исключения.
  • ExceptionDispatchInfo.SourceException Используйте свойство для проверки захваченного исключения.

В следующем примере показано, как ExceptionDispatchInfo можно использовать класс и как выглядеть выходные данные.

ExceptionDispatchInfo? edi = null; try < var txt = File.ReadAllText(@"C:\temp\file.txt"); >catch (FileNotFoundException e) < edi = ExceptionDispatchInfo.Capture(e); >// . Console.WriteLine("I was here."); if (edi is not null) edi.Throw(); 

Если файл в примере кода не существует, создается следующий результат:

I was here. Unhandled exception. System.IO.FileNotFoundException: Could not find file 'C:\temp\file.txt'. File name: 'C:\temp\file.txt' at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options) at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode) at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode) at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode) at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding encoding, Int32 bufferSize) at System.IO.File.ReadAllText(String path, Encoding encoding) at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 12 --- End of stack trace from previous location --- at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 24 

См. также

Совместная работа с нами на GitHub

Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.

Обработка исключений

Иногда при выполнении программы возникают ошибки, которые трудно предусмотреть или предвидеть, а иногда и вовсе невозможно. Например, при передачи файла по сети может неожиданно оборваться сетевое подключение. такие ситуации называются исключениями . Язык C# предоставляет разработчикам возможности для обработки таких ситуаций. Для этого в C# предназначена конструкция try. catch. finally .

try < >catch < >finally

При использовании блока try. catch..finally вначале выполняются все инструкции в блоке try . Если в этом блоке не возникло исключений, то после его выполнения начинает выполняться блок finally . И затем конструкция try..catch..finally завершает свою работу.

Если же в блоке try вдруг возникает исключение, то обычный порядок выполнения останавливается, и среда CLR начинает искать блок catch , который может обработать данное исключение. Если нужный блок catch найден, то он выполняется, и после его завершения выполняется блок finally.

Если нужный блок catch не найден, то при возникновении исключения программа аварийно завершает свое выполнение.

Рассмотрим следующий пример:

int x = 5; int y = x / 0; Console.WriteLine($"Результат: "); Console.WriteLine("Конец программы");

В данном случае происходит деление числа на 0, что приведет к генерации исключения. И при запуске приложения в режиме отладки мы увидим в Visual Studio окошко, которое информирует об исключении:

Исключения в C#

В этом окошке мы видим, что возникло исключение, которое представляет тип System.DivideByZeroException , то есть попытка деления на ноль. С помощью пункта View Details можно посмотреть более детальную информацию об исключении.

И в этом случае единственное, что нам остается, это завершить выполнение программы.

Чтобы избежать подобного аварийного завершения программы, следует использовать для обработки исключений конструкцию try. catch. finally . Так, перепишем пример следующим образом:

try < int x = 5; int y = x / 0; Console.WriteLine($"Результат: "); > catch < Console.WriteLine("Возникло исключение!"); >finally < Console.WriteLine("Блок finally"); >Console.WriteLine("Конец программы");

В данном случае у нас опять же возникнет исключение в блоке try, так как мы пытаемся разделить на ноль. И дойдя до строки

int y = x / 0;

выполнение программы остановится. CLR найдет блок catch и передаст управление этому блоку.

После блока catch будет выполняться блок finally.

Возникло исключение! Блок finally Конец программы

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

Следует отметить, что в этой конструкции обязателен блок try . При наличии блока catch мы можем опустить блок finally:

try < int x = 5; int y = x / 0; Console.WriteLine($"Результат: "); > catch

И, наоборот, при наличии блока finally мы можем опустить блок catch и не обрабатывать исключение:

try < int x = 5; int y = x / 0; Console.WriteLine($"Результат: "); > finally

Однако, хотя с точки зрения синтаксиса C# такая конструкция вполне корректна, тем не менее, поскольку CLR не сможет найти нужный блок catch, то исключение не будет обработано, и программа аварийно завершится.

Обработка исключений и условные конструкции

Ряд исключительных ситуаций может быть предвиден разработчиком. Например, пусть в программе есть метод, который принимает строку, конвертирует ее в число и вычисляет квадрат этого числа:

Square("12"); // Квадрат числа 12: 144 Square("ab"); // !Исключение void Square(string data) < int x = int.Parse(data); Console.WriteLine($"Квадрат числа : "); >

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

Square("12"); // Квадрат числа 12: 144 Square("ab"); // Некорректный ввод void Square(string data) < if (int.TryParse(data, out var x)) < Console.WriteLine($"Квадрат числа : "); > else < Console.WriteLine("Некорректный ввод"); >>

Метод int.TryParse() возвращает true , если преобразование можно осуществить, и false — если нельзя. При допустимости преобразования переменная x будет содержать введенное число. Так, не используя try. catch можно обработать возможную исключительную ситуацию.

С точки зрения производительности использование блоков try..catch более накладно, чем применение условных конструкций. Поэтому по возможности вместо try..catch лучше использовать условные конструкции на проверку исключительных ситуаций.

  • Глава 1. Введение в C#
    • Язык C# и платформа .NET
    • Первая программа на C# с .NET CLI
    • Начало работы с Visual Studio. Первая программа
    • Первое приложение в WSL
    • Структура программы
    • Переменные и константы
    • Литералы
    • Типы данных
    • Консольный ввод-вывод
    • Арифметические операции
    • Поразрядные операции
    • Операции присваивания
    • Преобразования базовых типов данных
    • Условные выражения
    • Конструкция if..else и тернарная операция
    • Циклы
    • Массивы
    • Задачи с массивами
    • Методы
    • Параметры методов
    • Возвращение значения и оператор return
    • Передача параметров по ссылке и значению. Выходные параметры
    • Массив параметров и ключевое слово params
    • Рекурсивные функции
    • Локальные функции
    • Конструкция switch
    • Перечисления enum
    • Классы и объекты
    • Конструкторы, инициализаторы и деконструкторы
    • Класс Program и метод Main. Программы верхнего уровня
    • Структуры
    • Типы значений и ссылочные типы
    • Область видимости (контекст) переменных
    • Пространства имен
    • Глобальные пространства имен
    • Подключение пространств имен по умолчанию
    • Создание библиотеки классов в Visual Studio
    • Создание библиотеки классов с помощью .NET CLI
    • Модификаторы доступа
    • Свойства
    • Перегрузка методов
    • Статические члены и модификатор static
    • Установка пакетов Nuget
    • Константы, поля и структуры для чтения
    • Null и ссылочные типы
    • Null и значимые типы
    • Проверка на null, операторы ?. и ??
    • Псевдонимы типов и статический импорт
    • Наследование
    • Преобразование типов
    • Виртуальные методы и свойства
    • Скрытие методов и свойств
    • Различие переопределения и скрытия методов
    • Абстрактные классы
    • Класс System.Object и его методы
    • Обобщенные типы
    • Ограничения обобщений
    • Наследование обобщенных типов
    • Конструкция try..catch..finally
    • Блок catch и фильтры исключений
    • Типы исключений. Класс Exception
    • Генерация исключения и оператор throw
    • Создание классов исключений
    • Поиск блока catch при обработке исключений
    • Делегаты
    • Применение делегатов
    • Анонимные методы
    • Лямбды
    • События
    • Ковариантность и контравариантность делегатов
    • Делегаты Action, Predicate и Func
    • Замыкания
    • Определение интерфейсов
    • Применение интерфейсов
    • Явная реализация интерфейсов
    • Реализация интерфейсов в базовых и производных классах
    • Наследование интерфейсов
    • Интерфейсы в обобщениях
    • Копирование объектов. Интерфейс ICloneable
    • Сортировка объектов. Интерфейс IComparable
    • Ковариантность и контравариантность обобщенных интерфейсов
    • Определение операторов
    • Перегрузка операций преобразования типов
    • Индексаторы
    • Переменные-ссылки и возвращение ссылки
    • Методы расширения
    • Частичные классы и методы
    • Анонимные типы
    • Кортежи
    • Records
    • Паттерн типов
    • Паттерн свойств
    • Паттерны кортежей
    • Позиционный паттерн
    • Реляционный и логический паттерны
    • Паттерны списков
    • Список List
    • Двухсвязный список LinkedList
    • Очередь Queue
    • Стек Stack
    • Словарь Dictionary
    • Класс ObservableCollection
    • Интерфейсы IEnumerable и IEnumerator
    • Итераторы и оператор yield
    • Класс Array и массивы
    • Span
    • Индексы и диапазоны
    • Строки и класс System.String
    • Операции со строками
    • Форматирование и интерполяция строк
    • Класс StringBuilder
    • Регулярные выражения
    • Структура DateTime
    • Форматирование дат и времени
    • DateOnly и TimeOnly
    • Отложенная инициализация и тип Lazy
    • Математические вычисления и класс Math
    • Преобразование типов и класс Convert
    • Введение в многопоточность. Класс Thread
    • Создание потоков. Делегат ThreadStart
    • Потоки с параметрами и ParameterizedThreadStart
    • Синхронизация потоков
    • Мониторы
    • Класс AutoResetEvent
    • Мьютексы
    • Семафоры
    • Задачи и класс Task
    • Работа с классом Task
    • Задачи продолжения
    • Класс Parallel
    • Отмена задач и параллельных операций. CancellationToken
    • Асинхронные методы, async и await
    • Возвращение результата из асинхронного метода
    • Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAny
    • Обработка ошибок в асинхронных методах
    • Асинхронные стримы
    • Основы LINQ
    • Проекция данных
    • Фильтрация коллекции
    • Сортировка
    • Объединение, пересечение и разность коллекций
    • Агрегатные операции
    • Получение части коллекции
    • Группировка
    • Соединение коллекций
    • Проверка наличия и получение элементов
    • Отложенное и немедленное выполнение LINQ
    • Делегаты в запросах LINQ
    • Введение в Parallel LINQ. Метод AsParallel
    • Метод AsOrdered
    • Обработка ошибок и отмена параллельных операции
    • Введение в рефлексию. Класс System.Type
    • Применение рефлексии и исследование типов
    • Исследование методов и конструкторов с помощью рефлексии
    • Исследование полей и свойств с помощью рефлексии
    • Динамическая загрузка сборок и позднее связывание
    • Атрибуты в .NET
    • DLR в C#. Ключевое слово dynamic
    • DynamicObject и ExpandoObject
    • Использование IronPython в .NET
    • Сборщик мусора в C#
    • Финализируемые объекты. Метод Dispose
    • Конструкция using
    • Указатели
    • Указатели на структуры, члены классов и массивы
    • Работа с дисками
    • Работа с каталогами
    • Работа с файлами. Классы File и FileInfo
    • FileStream. Чтение и запись файла
    • Чтение и запись текстовых файлов. StreamReader и StreamWriter
    • Бинарные файлы. BinaryWriter и BinaryReader
    • Архивация и сжатие файлов
    • Сериализация в JSON. JsonSerializer
    • XML-Документы
    • Работа с XML с помощью System.Xml
    • Изменение XML-документа
    • XPath
    • Linq to Xml. Создание Xml-документа
    • Выборка элементов в LINQ to XML
    • Изменение документа в LINQ to XML
    • Сериализация в XML. XmlSerializer
    • Процессы
    • Домены приложений
    • AssemblyLoadContext и динамическая загрузка и выгрузка сборок
    • Native AOT
    • Нововведения в C# 11
    • Нововведения в C# 12

    Помощь сайту
    410011174743222
    Перевод на карту
    Номер карты:
    4048415020898850

    Контакты для связи: metanit22@mail.ru

    Copyright © metanit.com, 2024. Все права защищены.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *