.NET Framework
Исключения
Поиск…
замечания
Связанные с:
Ловля исключения
Код может и должен вызывать исключения в исключительных обстоятельствах. Примеры этого включают:
- Попытка прочитать конец конца потока
- Отсутствие необходимых разрешений для доступа к файлу
- Попытка выполнить недопустимую операцию, например деление на ноль
- Тайм-аут, возникающий при загрузке файла из Интернета
Вызывающий может обрабатывать эти исключения, «ловя их», и должен делать это только тогда, когда:
- Он может фактически разрешить исключительные обстоятельства или восстановить надлежащим образом;
- Это может обеспечить дополнительный контекст для исключения, которое было бы полезно, если бы исключение нужно было перебросить (повторные выбросы исключений улавливаются обработчиками исключений далее в стеке вызовов)
Следует отметить, что выбор не для того, чтобы поймать исключение, является вполне допустимым, если намерение заключается в том, чтобы его обрабатывали на более высоком уровне.
Захват исключения выполняется путем переноса потенциально метательного кода в блок try { ... }
следующим образом и улавливания исключений, которые он может обрабатывать в блоке catch (ExceptionType) { ... }
:
Console.Write("Please enter a filename: ");
string filename = Console.ReadLine();
Stream fileStream;
try
{
fileStream = File.Open(filename);
}
catch (FileNotFoundException)
{
Console.WriteLine("File '{0}' could not be found.", filename);
}
Использование блока finally
finally { ... }
блок try-finally
или try-catch-finally
всегда будет выполняться независимо от того, произошло ли исключение или нет (кроме случаев, когда StackOverflowException
было выбрано или вызвано в Environment.FailFast()
).
Его можно использовать для бесплатной или очистки ресурсов, полученных в блоке try { ... }
.
Console.Write("Please enter a filename: ");
string filename = Console.ReadLine();
Stream fileStream = null;
try
{
fileStream = File.Open(filename);
}
catch (FileNotFoundException)
{
Console.WriteLine("File '{0}' could not be found.", filename);
}
finally
{
if (fileStream != null)
{
fileStream.Dispose();
}
}
Ловля и реорганизация пойманных исключений
Если вы хотите поймать исключение и что-то сделать, но вы не можете продолжить выполнение текущего блока кода из-за исключения, вы можете захотеть перебросить исключение в следующий обработчик исключений в стеке вызовов. Есть хорошие способы и плохие способы сделать это.
private static void AskTheUltimateQuestion()
{
try
{
var x = 42;
var y = x / (x - x); // will throw a DivideByZeroException
// IMPORTANT NOTE: the error in following string format IS intentional
// and exists to throw an exception to the FormatException catch, below
Console.WriteLine("The secret to life, the universe, and everything is {1}", y);
}
catch (DivideByZeroException)
{
// we do not need a reference to the exception
Console.WriteLine("Dividing by zero would destroy the universe.");
// do this to preserve the stack trace:
throw;
}
catch (FormatException ex)
{
// only do this if you need to change the type of the Exception to be thrown
// and wrap the inner Exception
// remember that the stack trace of the outer Exception will point to the
// next line
// you'll need to examine the InnerException property to get the stack trace
// to the line that actually started the problem
throw new InvalidOperationException("Watch your format string indexes.", ex);
}
catch (Exception ex)
{
Console.WriteLine("Something else horrible happened. The exception: " + ex.Message);
// do not do this, because the stack trace will be changed to point to
// this location instead of the location where the exception
// was originally thrown:
throw ex;
}
}
static void Main()
{
try
{
AskTheUltimateQuestion();
}
catch
{
// choose this kind of catch if you don't need any information about
// the exception that was caught
// this block "eats" all exceptions instead of rethrowing them
}
}
Вы можете фильтровать по типу исключений и даже по свойствам исключений (новый в C # 6.0, более длинный доступный в VB.NET (ссылка)):
Документация / C # / новые функции
Исключительные фильтры
Поскольку исключения C # 6.0 могут быть отфильтрованы с использованием оператора when
.
Это похоже на использование простого if
но не разматывает стек, если условие внутри when
не выполняется.
пример
try
{
// ...
}
catch (Exception e) when (e.InnerException != null) // Any condition can go in here.
{
// ...
}
Такую информацию можно найти в функциях C # 6.0 здесь: Исключительные фильтры
Повторное исключение в блоке catch
В блоке catch
ключевое слово throw
может использоваться самостоятельно, без указания значения исключения, для восстановления только что пойманного исключения. Повторное исключение позволяет исходному исключению продолжать цепочку обработки исключений, сохраняя стек вызовов или связанные данные:
try {...}
catch (Exception ex) {
// Note: the ex variable is *not* used
throw;
}
Общим анти-шаблоном является вместо этого throw ex
, что приводит к ограничению зрения следующего обработчика исключения для трассировки стека:
try {...}
catch (Exception ex) {
// Note: the ex variable is thrown
// future stack traces of the exception will not see prior calls
throw ex;
}
В общем случае использование throw ex
нежелательно, так как будущие обработчики исключений, которые проверяют трассировку стека, смогут видеть вызовы еще до throw ex
. Опуская переменную ex
и используя ключевое слово throw
исходное исключение будет «пузыряться» .
Выбрасывание исключения из другого метода при сохранении его информации
Иногда вы хотите поймать исключение и выбросить его из другого потока или метода, сохраняя исходный стек исключений. Это можно сделать с помощью ExceptionDispatchInfo
:
using System.Runtime.ExceptionServices;
void Main()
{
ExceptionDispatchInfo capturedException = null;
try
{
throw new Exception();
}
catch (Exception ex)
{
capturedException = ExceptionDispatchInfo.Capture(ex);
}
Foo(capturedException);
}
void Foo(ExceptionDispatchInfo exceptionDispatchInfo)
{
// Do stuff
if (capturedException != null)
{
// Exception stack trace will show it was thrown from Main() and not from Foo()
exceptionDispatchInfo.Throw();
}
}