Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье приводятся дополнительные замечания к справочной документации по этому API.
Класс Exception является базовым классом для всех исключений. При возникновении ошибки система или приложение, выполняющееся в данный момент, выдает исключение, содержащее сведения об ошибке. После возникновения исключения он обрабатывается приложением или обработчиком исключений по умолчанию.
Ошибки и исключения
Ошибки во время выполнения могут возникать по различным причинам. Однако не все ошибки должны обрабатываться как исключения в коде. Ниже приведены некоторые категории ошибок, которые могут возникать во время выполнения и соответствующие способы реагирования на них.
Ошибки использования. Ошибка использования представляет ошибку в логике программы, которая может привести к исключению. Однако ошибка должна быть устранена не через обработку исключений, а путем изменения кода сбоя. Например, в следующем примере переопределение метода Object.Equals(Object) предполагает, что аргумент
obj
всегда должен не быть null.using System; public class Person1 { private string _name; public string Name { get { return _name; } set { _name = value; } } public override int GetHashCode() { return this.Name.GetHashCode(); } public override bool Equals(object obj) { // This implementation contains an error in program logic: // It assumes that the obj argument is not null. Person1 p = (Person1) obj; return this.Name.Equals(p.Name); } } public class UsageErrorsEx1 { public static void Main() { Person1 p1 = new Person1(); p1.Name = "John"; Person1 p2 = null; // The following throws a NullReferenceException. Console.WriteLine($"p1 = p2: {p1.Equals(p2)}"); } }
// In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation contains an error in program logic: // It assumes that the obj argument is not null. let p = obj :?> Person this.Name.Equals p.Name let p1 = Person() p1.Name <- "John" let p2: Person = null // The following throws a NullReferenceException. printfn $"p1 = p2: {p1.Equals p2}"
Public Class Person Private _name As String Public Property Name As String Get Return _name End Get Set _name = value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation contains an error in program logic: ' It assumes that the obj argument is not null. Dim p As Person = CType(obj, Person) Return Me.Name.Equals(p.Name) End Function End Class Module Example2 Public Sub Main() Dim p1 As New Person() p1.Name = "John" Dim p2 As Person = Nothing ' The following throws a NullReferenceException. Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module
Исключение NullReferenceException, которое возникает, когда
obj
равноnull
, можно устранить, изменив исходный код для явного тестирования на null перед вызовом переопределения Object.Equals, а затем повторно скомпилировав. В следующем примере содержится исправленный исходныйnull
код, обрабатывающий аргумент.using System; public class Person2 { private string _name; public string Name { get { return _name; } set { _name = value; } } public override int GetHashCode() { return this.Name.GetHashCode(); } public override bool Equals(object obj) { // This implementation handles a null obj argument. Person2 p = obj as Person2; if (p == null) return false; else return this.Name.Equals(p.Name); } } public class UsageErrorsEx2 { public static void Main() { Person2 p1 = new Person2(); p1.Name = "John"; Person2 p2 = null; Console.WriteLine($"p1 = p2: {p1.Equals(p2)}"); } } // The example displays the following output: // p1 = p2: False
// In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation handles a null obj argument. match obj with | :? Person as p -> this.Name.Equals p.Name | _ -> false let p1 = Person() p1.Name <- "John" let p2: Person = null printfn $"p1 = p2: {p1.Equals p2}" // The example displays the following output: // p1 = p2: False
Public Class Person2 Private _name As String Public Property Name As String Get Return _name End Get Set _name = Value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation handles a null obj argument. Dim p As Person2 = TryCast(obj, Person2) If p Is Nothing Then Return False Else Return Me.Name.Equals(p.Name) End If End Function End Class Module Example3 Public Sub Main() Dim p1 As New Person2() p1.Name = "John" Dim p2 As Person2 = Nothing Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module ' The example displays the following output: ' p1 = p2: False
Вместо использования обработки исключений для ошибок использования можно использовать метод Debug.Assert для выявления ошибок в отладочных сборках и метод Trace.Assert для выявления таких ошибок как в отладочных, так и в производственных сборках. Дополнительные сведения см. в разделе "Утверждения" в управляемом коде.
Ошибки программы. Ошибка программы — это ошибка во время выполнения, которую не обязательно можно избежать путем написания кода без ошибок.
В некоторых случаях ошибка программы может отражать ожидаемое или обычное условие ошибки. В этом случае может потребоваться избежать использования обработки исключений для обработки ошибки программы и вместо этого повторить операцию. Например, если пользователь должен ввести дату в определенном формате, можно проанализировать строку даты, вызвав DateTime.TryParseExact метод, который возвращает Boolean значение, указывающее, успешно ли выполнена операция синтаксического анализа, а не с помощью DateTime.ParseExact метода, которое создает FormatException исключение, если строка даты не может быть преобразована в DateTime значение. Аналогичным образом, если пользователь пытается открыть файл, который не существует, можно сначала вызвать File.Exists метод, чтобы проверить, существует ли файл и, если это не так, предложите пользователю создать его.
В других случаях ошибка программы отражает непредвиденное условие ошибки, которое можно обрабатывать в коде. Например, даже если вы проверили, что файл существует, его можно удалить перед его открытием или может быть повреждено. В этом случае попытка открыть файл путем создания экземпляра StreamReader объекта или вызова Open метода может вызвать FileNotFoundException исключение. В этих случаях следует использовать обработку исключений для восстановления после ошибки.
Сбои системы. Сбой системы — это ошибка во время выполнения, которая не может обрабатываться программным способом. Например, любой OutOfMemoryException метод может вызвать исключение, если среда CLR не может выделить дополнительную память. Обычно системные сбои не обрабатываются с помощью обработки исключений. Вместо этого вы можете использовать такое событие, как AppDomain.UnhandledException и вызвать Environment.FailFast метод для регистрации сведений об исключении и уведомить пользователя о сбое до завершения приложения.
Блоки try/catch
Среда выполнения общего языка (CLR) предоставляет модель обработки исключений, основанную на представлении исключений в виде объектов, а также на разделении кода программы и кода обработки исключений на try
блоки и catch
блоки. Существует один или несколько catch
блоков, каждый из которых предназначен для обработки определенного типа исключения, или один блок, предназначенный для перехвата более конкретного исключения, чем другой блок.
Если приложение обрабатывает исключения, возникающие во время выполнения блока кода приложения, код должен быть помещен в try
инструкцию и называется блоком try
. Код приложения, обрабатывающий исключения, создаваемые блоком try
, помещается в catch
инструкцию и называется блоком catch
. Ноль или несколько catch
блоков связаны с блоком try
, и каждый catch
блок включает фильтр типов, определяющий типы исключений, которые он обрабатывает.
При возникновении исключения в try
блоке система выполняет поиск связанных catch
блоков в том порядке, в котором они появляются в коде приложения, пока не будет найден catch
блок, обрабатывающий исключение. Блок catch
обрабатывает исключение типа T
, если фильтр типа блока catch указывает T
или любой тип, производный от T
. Система перестает выполнять поиск после того, как он находит первый catch
блок, обрабатывающий исключение. По этой причине в коде catch
приложения необходимо указать блок, обрабатывающий тип, перед блоком catch
, обрабатывающим его базовые типы, как показано в примере, приведенном в этом разделе. Блок catch, который обрабатывает System.Exception
, указан последним.
Если ни один из catch
блоков, связанных с текущим try
блоком, не обрабатывает исключение, и текущий try
блок вложен в другие try
блоки в текущем вызове, выполняется поиск блоков, связанных со следующим заключающим try
блоком. Если блок, соответствующий исключению, не catch
найден, система выполняет поиск по предыдущим уровням вложенности в текущем вызове. Если блок catch
для исключения не найден в текущем вызове, исключение передается вверх по стеку вызовов, и в предыдущем кадре стека выполняется поиск блока catch
, обрабатывающего исключение. Поиск стека вызовов продолжается до тех пор, пока исключение не будет обработано или пока в стеке вызовов не будет более кадров. Если верхняя часть стека вызовов достигается без поиска catch
блока, обрабатывающего исключение, обработчик исключений по умолчанию обрабатывает его, и приложение завершает работу.
Выражение try..with в F#
F# не использует catch
блоки. Вместо этого исключение сопоставляется с шаблоном с помощью одного with
блока. Так как это выражение, а не оператор, все пути должны возвращать один и тот же тип. Дополнительные сведения см. в статье "Выражение try...with".
Функции типа исключений
Типы исключений поддерживают следующие функции:
Читаемый человеком текст, описывающий ошибку. При возникновении исключения среда выполнения делает текстовое сообщение доступным для информирования пользователя о характере ошибки и предложить действие для устранения проблемы. Это текстовое сообщение хранится в свойстве Message объекта исключения. Во время создания объекта исключения можно передать текстовую строку конструктору, чтобы описать сведения об этом конкретном исключении. Если аргумент сообщения об ошибке не указан конструктору, используется сообщение об ошибке по умолчанию. Дополнительные сведения см. в свойстве Message.
Состояние стека вызовов при возникновении исключения. Свойство StackTrace содержит трассировку стека, которую можно использовать для определения места возникновения ошибки в коде. Трассировка стека перечисляет все вызываемые методы и номера строк в исходном файле, где выполняются вызовы.
Свойства класса исключений
Класс Exception содержит ряд свойств, которые помогают определить расположение кода, тип, файл справки и причину исключения: StackTrace, , HResultInnerExceptionHelpLinkSourceMessageTargetSiteи .Data
Если между двумя или более исключениями существует причинная связь, InnerException свойство сохраняет эту информацию. Внешнее исключение создается в ответ на это внутреннее исключение. Код, обрабатывающий внешнее исключение, может использовать сведения из предыдущего внутреннего исключения для более правильной обработки ошибки. Дополнительные сведения об исключении можно хранить в виде коллекции пар "ключ-значение" в свойстве Data .
Строка сообщения об ошибке, передаваемая конструктору во время создания объекта исключения, должна быть локализована и может быть предоставлена из файла ресурсов с помощью ResourceManager класса. Дополнительные сведения о локализованных ресурсах см. в разделах о создании сателлитных сборок и упаковке и развертывании ресурсов.
Чтобы предоставить пользователю подробные сведения о том, почему произошло исключение, свойство HelpLink может содержать URL-адрес (или URN) на файл справки.
Класс Exception использует HRESULT COR_E_EXCEPTION
, имеющий значение 0x80131500.
Список начальных значений свойств для экземпляра Exception класса см. в Exception конструкторах.
Вопросы, связанные с производительностью
Создание или обработка исключения потребляет значительное количество системных ресурсов и времени выполнения. Создание исключений только для обработки действительно чрезвычайных условий, а не для обработки прогнозируемых событий или управления потоком. Например, в некоторых случаях, например при разработке библиотеки классов, можно вызвать исключение, если аргумент метода недопустим, так как предполагается, что метод будет вызываться с допустимыми параметрами. Недопустимый аргумент метода, если это не результат ошибки использования, означает, что произошло что-то необычное. И наоборот, не вызывайте исключение, если входные данные пользователя недопустимы, так как пользователи могут иногда вводить недопустимые данные. Вместо этого предоставьте механизм повтора, чтобы пользователи могли вводить допустимые входные данные. Не следует использовать исключения для обработки ошибок использования. Вместо этого используйте утверждения для выявления и исправления ошибок использования.
Кроме того, не создавайте исключение, если код возврата достаточен; не преобразуйте возвращаемый код в исключение; и не регулярно перехватывайте исключение, игнорируя его, а затем не продолжайте обработку.
Повторно выбросить исключение
Во многих случаях обработчик исключений просто хочет передать исключение вызывающему. Это чаще всего происходит в:
Библиотека классов, которая в свою очередь упаковывает вызовы методов в библиотеку классов .NET или другие библиотеки классов.
Приложение или библиотека, столкнувшиеся с фатальным исключением. Обработчик исключений может регистрировать исключение, а затем повторно создавать исключение.
Рекомендуемый способ повторного выброса исключения — просто использовать инструкцию throw в C#, функцию reraise в F# и инструкцию Throw в Visual Basic без включения выражения. Это гарантирует сохранение всех сведений о стеке вызовов при распространении исключения на вызывающий объект. В следующем примере показано это. Метод расширения строки FindOccurrences
оборачивает один или несколько вызовов String.IndexOf(String, Int32) без предварительной проверки их аргументов.
using System;
using System.Collections.Generic;
public static class Library1
{
public static int[] FindOccurrences(this String s, String f)
{
var indexes = new List<int>();
int currentIndex = 0;
try
{
while (currentIndex >= 0 && currentIndex < s.Length)
{
currentIndex = s.IndexOf(f, currentIndex);
if (currentIndex >= 0)
{
indexes.Add(currentIndex);
currentIndex++;
}
}
}
catch (ArgumentNullException)
{
// Perform some action here, such as logging this exception.
throw;
}
return indexes.ToArray();
}
}
open System
module Library =
let findOccurrences (s: string) (f: string) =
let indexes = ResizeArray()
let mutable currentIndex = 0
try
while currentIndex >= 0 && currentIndex < s.Length do
currentIndex <- s.IndexOf(f, currentIndex)
if currentIndex >= 0 then
indexes.Add currentIndex
currentIndex <- currentIndex + 1
with :? ArgumentNullException ->
// Perform some action here, such as logging this exception.
reraise ()
indexes.ToArray()
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Public Module Library
<Extension()>
Public Function FindOccurrences1(s As String, f As String) As Integer()
Dim indexes As New List(Of Integer)
Dim currentIndex As Integer = 0
Try
Do While currentIndex >= 0 And currentIndex < s.Length
currentIndex = s.IndexOf(f, currentIndex)
If currentIndex >= 0 Then
indexes.Add(currentIndex)
currentIndex += 1
End If
Loop
Catch e As ArgumentNullException
' Perform some action here, such as logging this exception.
Throw
End Try
Return indexes.ToArray()
End Function
End Module
Затем вызывающий объект вызывает FindOccurrences
дважды. Во втором вызове FindOccurrences
, вызывающий объект передает null
в качестве строки поиска, что приводит к возникновению исключения ArgumentNullException в методе String.IndexOf(String, Int32). Это исключение обрабатывается методом FindOccurrences
и передается обратно вызывающей стороне. Так как оператор throw используется без выражения, выходные данные из примера показывают, что стек вызовов сохраняется.
public class RethrowEx1
{
public static void Main()
{
String s = "It was a cold day when...";
int[] indexes = s.FindOccurrences("a");
ShowOccurrences(s, "a", indexes);
Console.WriteLine();
String toFind = null;
try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine($"An exception ({e.GetType().Name}) occurred.");
Console.WriteLine($"Message:{Environment.NewLine} {e.Message}{Environment.NewLine}");
Console.WriteLine($"Stack Trace:{Environment.NewLine} {e.StackTrace}{Environment.NewLine}");
}
}
private static void ShowOccurrences(String s, String toFind, int[] indexes)
{
Console.Write("'{0}' occurs at the following character positions: ",
toFind);
for (int ctr = 0; ctr < indexes.Length; ctr++)
Console.Write("{0}{1}", indexes[ctr],
ctr == indexes.Length - 1 ? "" : ", ");
Console.WriteLine();
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message:
// Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
open Library
let showOccurrences toFind (indexes: int[]) =
printf $"'{toFind}' occurs at the following character positions: "
for i = 0 to indexes.Length - 1 do
printf $"""{indexes[i]}{if i = indexes.Length - 1 then "" else ", "}"""
printfn ""
let s = "It was a cold day when..."
let indexes = findOccurrences s "a"
showOccurrences "a" indexes
printfn ""
let toFind: string = null
try
let indexes = findOccurrences s toFind
showOccurrences toFind indexes
with :? ArgumentNullException as e ->
printfn $"An exception ({e.GetType().Name}) occurred."
printfn $"Message:\n {e.Message}\n"
printfn $"Stack Trace:\n {e.StackTrace}\n"
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message:
// Value cannot be null. (Parameter 'value')
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.findOccurrences(String s, String f)
// at <StartupCode$fs>.main@()
Module Example1
Public Sub Main()
Dim s As String = "It was a cold day when..."
Dim indexes() As Integer = s.FindOccurrences1("a")
ShowOccurrences(s, "a", indexes)
Console.WriteLine()
Dim toFind As String = Nothing
Try
indexes = s.FindOccurrences1(toFind)
ShowOccurrences(s, toFind, indexes)
Catch e As ArgumentNullException
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name)
Console.WriteLine("Message:{0} {1}{0}", vbCrLf, e.Message)
Console.WriteLine("Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace)
End Try
End Sub
Private Sub ShowOccurrences(s As String, toFind As String, indexes As Integer())
Console.Write("'{0}' occurs at the following character positions: ",
toFind)
For ctr As Integer = 0 To indexes.Length - 1
Console.Write("{0}{1}", indexes(ctr),
If(ctr = indexes.Length - 1, "", ", "))
Next
Console.WriteLine()
End Sub
End Module
' The example displays the following output:
' 'a' occurs at the following character positions: 4, 7, 15
'
' An exception (ArgumentNullException) occurred.
' Message:
' Value cannot be null.
' Parameter name: value
'
' Stack Trace:
' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
' ngComparison comparisonType)
' at Library.FindOccurrences(String s, String f)
' at Example.Main()
В отличие от этого, если исключение выбрасывается повторно с помощью следующего оператора:
throw e;
Throw e
raise e
... Затем полный стек вызовов не сохраняется, и в примере будет создан следующий результат:
'a' occurs at the following character positions: 4, 7, 15
An exception (ArgumentNullException) occurred.
Message:
Value cannot be null.
Parameter name: value
Stack Trace:
at Library.FindOccurrences(String s, String f)
at Example.Main()
Немного более сложной альтернативой является создание нового исключения и сохранение сведений о стеке вызовов исходного исключения во внутреннем исключении. Вызывающая сторона может использовать свойство нового исключения InnerException для получения стека фреймов и другой информации об исходном исключении. В этом случае оператор throw выглядит следующим образом:
throw new ArgumentNullException("You must supply a search string.", e);
raise (ArgumentNullException("You must supply a search string.", e) )
Throw New ArgumentNullException("You must supply a search string.",
e)
Пользовательский код, обрабатывающий исключение, должен знать, что InnerException свойство содержит сведения о исходном исключении, как показано в следующем обработчике исключений.
try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine($"An exception ({e.GetType().Name}) occurred.");
Console.WriteLine($" Message:{Environment.NewLine}{e.Message}");
Console.WriteLine($" Stack Trace:{Environment.NewLine} {e.StackTrace}");
Exception ie = e.InnerException;
if (ie != null)
{
Console.WriteLine(" The Inner Exception:");
Console.WriteLine($" Exception Name: {ie.GetType().Name}");
Console.WriteLine($" Message: {ie.Message}{Environment.NewLine}");
Console.WriteLine($" Stack Trace:{Environment.NewLine} {ie.StackTrace}{Environment.NewLine}");
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message: You must supply a search string.
//
// Stack Trace:
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
//
// The Inner Exception:
// Exception Name: ArgumentNullException
// Message: Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
try
let indexes = findOccurrences s toFind
showOccurrences toFind indexes
with :? ArgumentNullException as e ->
printfn $"An exception ({e.GetType().Name}) occurred."
printfn $" Message:\n{e.Message}"
printfn $" Stack Trace:\n {e.StackTrace}"
let ie = e.InnerException
if ie <> null then
printfn " The Inner Exception:"
printfn $" Exception Name: {ie.GetType().Name}"
printfn $" Message: {ie.Message}\n"
printfn $" Stack Trace:\n {ie.StackTrace}\n"
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message: You must supply a search string.
//
// Stack Trace:
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
//
// The Inner Exception:
// Exception Name: ArgumentNullException
// Message: Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
Try
indexes = s.FindOccurrences(toFind)
ShowOccurrences(s, toFind, indexes)
Catch e As ArgumentNullException
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name)
Console.WriteLine(" Message: {1}{0}", vbCrLf, e.Message)
Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace)
Dim ie As Exception = e.InnerException
If ie IsNot Nothing Then
Console.WriteLine(" The Inner Exception:")
Console.WriteLine(" Exception Name: {0}", ie.GetType().Name)
Console.WriteLine(" Message: {1}{0}", vbCrLf, ie.Message)
Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, ie.StackTrace)
End If
End Try
' The example displays the following output:
' 'a' occurs at the following character positions: 4, 7, 15
'
' An exception (ArgumentNullException) occurred.
' Message: You must supply a search string.
'
' Stack Trace:
' at Library.FindOccurrences(String s, String f)
' at Example.Main()
'
' The Inner Exception:
' Exception Name: ArgumentNullException
' Message: Value cannot be null.
' Parameter name: value
'
' Stack Trace:
' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
' ngComparison comparisonType)
' at Library.FindOccurrences(String s, String f)
Выбор стандартных исключений
При возникновении исключения часто можно использовать существующий тип исключения в .NET вместо реализации пользовательского исключения. В этих двух условиях следует использовать стандартный тип исключения:
Возникает исключение, вызванное ошибкой использования (т. е. ошибкой в логике программы, созданной разработчиком, вызывающим метод). Как правило, вы создаете исключение, например ArgumentException, ArgumentNullExceptionили InvalidOperationExceptionNotSupportedException. Строка, которую вы предоставляете конструктору объекта исключения при создании экземпляра объекта исключения, должна описать ошибку, чтобы разработчик смог исправить его. Дополнительные сведения см. в свойстве Message.
Вы обрабатываете ошибку, которая может быть сообщена вызывающей стороне с помощью существующего исключения .NET. Необходимо выбросить наиболее производное исключение. Например, если метод требует, чтобы аргумент был допустимым членом типа перечисления, следует выбросить исключение InvalidEnumArgumentException (наиболее производный класс), а не исключение ArgumentException.
В следующей таблице перечислены распространенные типы исключений и условия, при которых их следует выбрасывать.
Исключение | Состояние |
---|---|
ArgumentException | Недопустимый ненулевой аргумент, который передается методу. |
ArgumentNullException | Аргумент, передаваемый методу null . |
ArgumentOutOfRangeException | Аргумент находится вне диапазона допустимых значений. |
DirectoryNotFoundException | Часть пути к каталогу недопустима. |
DivideByZeroException | Знаменатель в операции целочисленного или Decimal деления равен нулю. |
DriveNotFoundException | Диск недоступен или не существует. |
FileNotFoundException | Файл не существует. |
FormatException | Значение не соответствует соответствующему формату, который необходимо преобразовать из строки методом преобразования, например Parse . |
IndexOutOfRangeException | Индекс находится за пределами границ массива или коллекции. |
InvalidOperationException | Вызов метода недопустим в текущем состоянии объекта. |
KeyNotFoundException | Не удается найти указанный ключ для доступа к элементу в коллекции. |
NotImplementedException | Метод или операция не реализованы. |
NotSupportedException | Метод или операция не поддерживается. |
ObjectDisposedException | Операция выполняется над объектом, который был уничтожен. |
OverflowException | Арифметическая, приведение или преобразовательная операция приводит к переполнению. |
PathTooLongException | Путь или имя файла превышает максимальную системную длину. |
PlatformNotSupportedException | Операция не поддерживается на текущей платформе. |
RankException | Массив с неправильным числом измерений передается методу. |
TimeoutException | Истек интервал времени, выделенный для операции. |
UriFormatException | Используется недопустимый универсальный идентификатор ресурса (URI). |
Реализация пользовательских исключений
В следующих случаях использование существующего исключения .NET для обработки условия ошибки недостаточно.
Если исключение отражает уникальную ошибку программы, которая не может быть сопоставлена с существующим исключением .NET.
Если исключение требует обработки, отличающейся от обработки, подходящей для существующего исключения .NET, или исключение должно быть разграничено от аналогичного исключения. Например, если при ArgumentOutOfRangeException разборе числового представления строки вне диапазона целевого целочисленного типа возникает исключение, то вы не должны использовать то же исключение для ошибки, вызванной тем, что вызывающий не предоставляет соответствующие ограниченные значения при вызове метода.
Класс Exception является базовым классом всех исключений в .NET. Многие производные классы зависят от наследуемого поведения членов класса Exception; они не переопределяют члены из Exception и не определяют никаких уникальных членов.
Чтобы определить собственный класс исключений:
Определение класса, наследуемого от Exception. При необходимости определите все уникальные элементы, необходимые для вашего класса, чтобы предоставить дополнительные сведения об исключении. Например, ArgumentException класс содержит ParamName свойство, указывающее имя параметра, аргумент которого вызвал исключение, а RegexMatchTimeoutException свойство содержит MatchTimeout свойство, указывающее интервал времени ожидания.
При необходимости переопределите унаследованные элементы, функциональность которых вы хотите изменить или модифицировать. Обратите внимание, что большинство существующих производных Exception классов не переопределяют поведение унаследованных элементов.
Определите, является ли настраиваемый объект исключения сериализуемым. Сериализация позволяет сохранять сведения об исключении и разрешать доступ к данным об исключениях сервером и прокси-сервером клиента в контексте удаленного взаимодействия. Чтобы сделать объект исключения сериализуемым, пометьте его атрибутом SerializableAttribute .
Определите конструкторы вашего класса исключения. Как правило, классы исключений имеют один или несколько следующих конструкторов:
Exception(), который использует значения по умолчанию для инициализации свойств нового объекта исключения.
Exception(String), который инициализирует новый объект исключения с указанным сообщением об ошибке.
Exception(String, Exception), который инициализирует новый объект исключения с указанным сообщением об ошибке и внутренним исключением.
Exception(SerializationInfo, StreamingContext)— конструктор
protected
, который инициализирует новый объект исключения из сериализованных данных. Этот конструктор следует реализовать, если вы решили сделать объект исключения сериализуемым.
В следующем примере показано использование пользовательского класса исключений. Он определяет исключение, которое возникает NotPrimeException
при попытке клиента получить последовательность простых чисел, указав начальное число, которое не является простым. Исключение определяет новое свойство, NonPrime
которое возвращает неисчислимое число, вызвавшее исключение. Помимо реализации защищенного конструктора без параметров и конструктора с параметрами SerializationInfo и StreamingContext для сериализации, класс NotPrimeException
определяет три дополнительных конструктора для поддержки свойства NonPrime
. Каждый конструктор вызывает конструктор базового класса, а также сохраняет значение непростого числа. Класс NotPrimeException
также помечается атрибутом SerializableAttribute .
using System;
using System.Runtime.Serialization;
[Serializable()]
public class NotPrimeException : Exception
{
private int notAPrime;
protected NotPrimeException()
: base()
{ }
public NotPrimeException(int value) :
base(String.Format("{0} is not a prime number.", value))
{
notAPrime = value;
}
public NotPrimeException(int value, string message)
: base(message)
{
notAPrime = value;
}
public NotPrimeException(int value, string message, Exception innerException) :
base(message, innerException)
{
notAPrime = value;
}
protected NotPrimeException(SerializationInfo info,
StreamingContext context)
: base(info, context)
{ }
public int NonPrime
{ get { return notAPrime; } }
}
namespace global
open System
open System.Runtime.Serialization
[<Serializable>]
type NotPrimeException =
inherit Exception
val notAPrime: int
member this.NonPrime =
this.notAPrime
new (value) =
{ inherit Exception($"%i{value} is not a prime number."); notAPrime = value }
new (value, message) =
{ inherit Exception(message); notAPrime = value }
new (value, message, innerException: Exception) =
{ inherit Exception(message, innerException); notAPrime = value }
// F# does not support protected members
new () =
{ inherit Exception(); notAPrime = 0 }
new (info: SerializationInfo, context: StreamingContext) =
{ inherit Exception(info, context); notAPrime = 0 }
Imports System.Runtime.Serialization
<Serializable()> _
Public Class NotPrimeException : Inherits Exception
Private notAPrime As Integer
Protected Sub New()
MyBase.New()
End Sub
Public Sub New(value As Integer)
MyBase.New(String.Format("{0} is not a prime number.", value))
notAPrime = value
End Sub
Public Sub New(value As Integer, message As String)
MyBase.New(message)
notAPrime = value
End Sub
Public Sub New(value As Integer, message As String, innerException As Exception)
MyBase.New(message, innerException)
notAPrime = value
End Sub
Protected Sub New(info As SerializationInfo,
context As StreamingContext)
MyBase.New(info, context)
End Sub
Public ReadOnly Property NonPrime As Integer
Get
Return notAPrime
End Get
End Property
End Class
Класс, показанный в следующем примере, использует решето Эратосфена для вычисления последовательности простых чисел от 2 до предела, указанного клиентом в вызове конструктора класса. Метод GetPrimesFrom
возвращает все простые числа, превышающие или равные указанному нижнему пределу, но генерирует NotPrimeException
, если этот нижний предел не является простым числом.
using System;
using System.Collections.Generic;
[Serializable]
public class PrimeNumberGenerator
{
private const int START = 2;
private int maxUpperBound = 10000000;
private int upperBound;
private bool[] primeTable;
private List<int> primes = new List<int>();
public PrimeNumberGenerator(int upperBound)
{
if (upperBound > maxUpperBound)
{
string message = String.Format(
"{0} exceeds the maximum upper bound of {1}.",
upperBound, maxUpperBound);
throw new ArgumentOutOfRangeException(message);
}
this.upperBound = upperBound;
// Create array and mark 0, 1 as not prime (True).
primeTable = new bool[upperBound + 1];
primeTable[0] = true;
primeTable[1] = true;
// Use Sieve of Eratosthenes to determine prime numbers.
for (int ctr = START; ctr <= (int)Math.Ceiling(Math.Sqrt(upperBound));
ctr++)
{
if (primeTable[ctr]) continue;
for (int multiplier = ctr; multiplier <= upperBound / ctr; multiplier++)
if (ctr * multiplier <= upperBound) primeTable[ctr * multiplier] = true;
}
// Populate array with prime number information.
int index = START;
while (index != -1)
{
index = Array.FindIndex(primeTable, index, (flag) => !flag);
if (index >= 1)
{
primes.Add(index);
index++;
}
}
}
public int[] GetAllPrimes()
{
return primes.ToArray();
}
public int[] GetPrimesFrom(int prime)
{
int start = primes.FindIndex((value) => value == prime);
if (start < 0)
throw new NotPrimeException(prime, String.Format("{0} is not a prime number.", prime));
else
return primes.FindAll((value) => value >= prime).ToArray();
}
}
namespace global
open System
[<Serializable>]
type PrimeNumberGenerator(upperBound) =
let start = 2
let maxUpperBound = 10000000
let primes = ResizeArray()
let primeTable =
upperBound + 1
|> Array.zeroCreate<bool>
do
if upperBound > maxUpperBound then
let message = $"{upperBound} exceeds the maximum upper bound of {maxUpperBound}."
raise (ArgumentOutOfRangeException message)
// Create array and mark 0, 1 as not prime (True).
primeTable[0] <- true
primeTable[1] <- true
// Use Sieve of Eratosthenes to determine prime numbers.
for i = start to float upperBound |> sqrt |> ceil |> int do
if not primeTable[i] then
for multiplier = i to upperBound / i do
if i * multiplier <= upperBound then
primeTable[i * multiplier] <- true
// Populate array with prime number information.
let mutable index = start
while index <> -1 do
index <- Array.FindIndex(primeTable, index, fun flag -> not flag)
if index >= 1 then
primes.Add index
index <- index + 1
member _.GetAllPrimes() =
primes.ToArray()
member _.GetPrimesFrom(prime) =
let start =
Seq.findIndex ((=) prime) primes
if start < 0 then
raise (NotPrimeException(prime, $"{prime} is not a prime number.") )
else
Seq.filter ((>=) prime) primes
|> Seq.toArray
Imports System.Collections.Generic
<Serializable()> Public Class PrimeNumberGenerator
Private Const START As Integer = 2
Private maxUpperBound As Integer = 10000000
Private upperBound As Integer
Private primeTable() As Boolean
Private primes As New List(Of Integer)
Public Sub New(upperBound As Integer)
If upperBound > maxUpperBound Then
Dim message As String = String.Format(
"{0} exceeds the maximum upper bound of {1}.",
upperBound, maxUpperBound)
Throw New ArgumentOutOfRangeException(message)
End If
Me.upperBound = upperBound
' Create array and mark 0, 1 as not prime (True).
ReDim primeTable(upperBound)
primeTable(0) = True
primeTable(1) = True
' Use Sieve of Eratosthenes to determine prime numbers.
For ctr As Integer = START To CInt(Math.Ceiling(Math.Sqrt(upperBound)))
If primeTable(ctr) Then Continue For
For multiplier As Integer = ctr To CInt(upperBound \ ctr)
If ctr * multiplier <= upperBound Then primeTable(ctr * multiplier) = True
Next
Next
' Populate array with prime number information.
Dim index As Integer = START
Do While index <> -1
index = Array.FindIndex(primeTable, index, Function(flag)
Return Not flag
End Function)
If index >= 1 Then
primes.Add(index)
index += 1
End If
Loop
End Sub
Public Function GetAllPrimes() As Integer()
Return primes.ToArray()
End Function
Public Function GetPrimesFrom(prime As Integer) As Integer()
Dim start As Integer = primes.FindIndex(Function(value)
Return value = prime
End Function)
If start < 0 Then
Throw New NotPrimeException(prime, String.Format("{0} is not a prime number.", prime))
Else
Return primes.FindAll(Function(value)
Return value >= prime
End Function).ToArray()
End If
End Function
End Class
В следующем примере выполняется два вызова GetPrimesFrom
метода с неисчислимыми числами, одна из которых пересекает границы домена приложения. В обоих случаях исключение создается и успешно обрабатывается в клиентском коде.
using System;
using System.Reflection;
class Example1
{
public static void Main()
{
int limit = 10000000;
PrimeNumberGenerator primes = new PrimeNumberGenerator(limit);
int start = 1000001;
try
{
int[] values = primes.GetPrimesFrom(start);
Console.WriteLine($"There are {start} prime numbers from {limit} to {2}");
}
catch (NotPrimeException e)
{
Console.WriteLine($"{e.NonPrime} is not prime");
Console.WriteLine(e);
Console.WriteLine("--------");
}
AppDomain domain = AppDomain.CreateDomain("Domain2");
PrimeNumberGenerator gen = (PrimeNumberGenerator)domain.CreateInstanceAndUnwrap(
typeof(Example).Assembly.FullName,
"PrimeNumberGenerator", true,
BindingFlags.Default, null,
new object[] { 1000000 }, null, null);
try
{
start = 100;
Console.WriteLine(gen.GetPrimesFrom(start));
}
catch (NotPrimeException e)
{
Console.WriteLine($"{e.NonPrime} is not prime");
Console.WriteLine(e);
Console.WriteLine("--------");
}
}
}
open System
open System.Reflection
let limit = 10000000
let primes = PrimeNumberGenerator limit
let start = 1000001
try
let values = primes.GetPrimesFrom start
printfn $"There are {values.Length} prime numbers from {start} to {limit}"
with :? NotPrimeException as e ->
printfn $"{e.NonPrime} is not prime"
printfn $"{e}"
printfn "--------"
let domain = AppDomain.CreateDomain "Domain2"
let gen =
domain.CreateInstanceAndUnwrap(
typeof<PrimeNumberGenerator>.Assembly.FullName,
"PrimeNumberGenerator", true,
BindingFlags.Default, null,
[| box 1000000 |], null, null)
:?> PrimeNumberGenerator
try
let start = 100
printfn $"{gen.GetPrimesFrom start}"
with :? NotPrimeException as e ->
printfn $"{e.NonPrime} is not prime"
printfn $"{e}"
printfn "--------"
Imports System.Reflection
Module Example
Sub Main()
Dim limit As Integer = 10000000
Dim primes As New PrimeNumberGenerator(limit)
Dim start As Integer = 1000001
Try
Dim values() As Integer = primes.GetPrimesFrom(start)
Console.WriteLine("There are {0} prime numbers from {1} to {2}",
start, limit)
Catch e As NotPrimeException
Console.WriteLine("{0} is not prime", e.NonPrime)
Console.WriteLine(e)
Console.WriteLine("--------")
End Try
Dim domain As AppDomain = AppDomain.CreateDomain("Domain2")
Dim gen As PrimeNumberGenerator = domain.CreateInstanceAndUnwrap(
GetType(Example).Assembly.FullName,
"PrimeNumberGenerator", True,
BindingFlags.Default, Nothing,
{1000000}, Nothing, Nothing)
Try
start = 100
Console.WriteLine(gen.GetPrimesFrom(start))
Catch e As NotPrimeException
Console.WriteLine("{0} is not prime", e.NonPrime)
Console.WriteLine(e)
Console.WriteLine("--------")
End Try
End Sub
End Module
' The example displays the following output:
' 1000001 is not prime
' NotPrimeException: 1000001 is not a prime number.
' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
' at Example.Main()
' --------
' 100 is not prime
' NotPrimeException: 100 is not a prime number.
' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
' at Example.Main()
' --------
Примеры
В следующем примере показан блок catch
(with
в F#), определенный для обработки ошибок ArithmeticException. Этот catch
блок также перехватывает DivideByZeroException ошибки, потому что DivideByZeroException происходит из ArithmeticException, и блок для DivideByZeroException ошибок явно не определен.
using System;
class ExceptionTestClass
{
public static void Main()
{
int x = 0;
try
{
int y = 100 / x;
}
catch (ArithmeticException e)
{
Console.WriteLine($"ArithmeticException Handler: {e}");
}
catch (Exception e)
{
Console.WriteLine($"Generic Exception Handler: {e}");
}
}
}
/*
This code example produces the following results:
ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
at ExceptionTestClass.Main()
*/
module ExceptionTestModule
open System
let x = 0
try
let y = 100 / x
()
with
| :? ArithmeticException as e ->
printfn $"ArithmeticException Handler: {e}"
| e ->
printfn $"Generic Exception Handler: {e}"
// This code example produces the following results:
// ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
// at <StartupCode$fs>.$ExceptionTestModule.main@()
Class ExceptionTestClass
Public Shared Sub Main()
Dim x As Integer = 0
Try
Dim y As Integer = 100 / x
Catch e As ArithmeticException
Console.WriteLine("ArithmeticException Handler: {0}", e.ToString())
Catch e As Exception
Console.WriteLine("Generic Exception Handler: {0}", e.ToString())
End Try
End Sub
End Class
'
'This code example produces the following results:
'
'ArithmeticException Handler: System.OverflowException: Arithmetic operation resulted in an overflow.
' at ExceptionTestClass.Main()
'