Прочитать на английском

Поделиться через


Класс System.Exception

В этой статье приводятся дополнительные замечания к справочной документации по этому API.

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

Ошибки и исключения

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

  • Ошибки использования. Ошибка использования представляет ошибку в логике программы, которая может привести к исключению. Однако ошибка должна быть устранена не через обработку исключений, а путем изменения кода сбоя. Например, в следующем примере переопределение метода Object.Equals(Object) предполагает, что аргумент obj всегда должен не быть null.

    C#
    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)}");
       }
    }
    

    Исключение NullReferenceException, которое возникает, когда obj равно null, можно устранить, изменив исходный код для явного тестирования на null перед вызовом переопределения Object.Equals, а затем повторно скомпилировав. В следующем примере содержится исправленный исходный null код, обрабатывающий аргумент.

    C#
    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
    

    Вместо использования обработки исключений для ошибок использования можно использовать метод 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) без предварительной проверки их аргументов.

C#
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();
    }
}

Затем вызывающий объект вызывает FindOccurrences дважды. Во втором вызове FindOccurrences, вызывающий объект передает null в качестве строки поиска, что приводит к возникновению исключения ArgumentNullException в методе String.IndexOf(String, Int32). Это исключение обрабатывается методом FindOccurrences и передается обратно вызывающей стороне. Так как оператор throw используется без выражения, выходные данные из примера показывают, что стек вызовов сохраняется.

C#
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()

В отличие от этого, если исключение выбрасывается повторно с помощью следующего оператора:

C#
throw 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 выглядит следующим образом:

C#
throw new ArgumentNullException("You must supply a search string.", e);

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

C#
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)

Выбор стандартных исключений

При возникновении исключения часто можно использовать существующий тип исключения в .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 и не определяют никаких уникальных членов.

Чтобы определить собственный класс исключений:

  1. Определение класса, наследуемого от Exception. При необходимости определите все уникальные элементы, необходимые для вашего класса, чтобы предоставить дополнительные сведения об исключении. Например, ArgumentException класс содержит ParamName свойство, указывающее имя параметра, аргумент которого вызвал исключение, а RegexMatchTimeoutException свойство содержит MatchTimeout свойство, указывающее интервал времени ожидания.

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

  3. Определите, является ли настраиваемый объект исключения сериализуемым. Сериализация позволяет сохранять сведения об исключении и разрешать доступ к данным об исключениях сервером и прокси-сервером клиента в контексте удаленного взаимодействия. Чтобы сделать объект исключения сериализуемым, пометьте его атрибутом SerializableAttribute .

  4. Определите конструкторы вашего класса исключения. Как правило, классы исключений имеют один или несколько следующих конструкторов:

    • Exception(), который использует значения по умолчанию для инициализации свойств нового объекта исключения.

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

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

    • Exception(SerializationInfo, StreamingContext)— конструктор protected , который инициализирует новый объект исключения из сериализованных данных. Этот конструктор следует реализовать, если вы решили сделать объект исключения сериализуемым.

В следующем примере показано использование пользовательского класса исключений. Он определяет исключение, которое возникает NotPrimeException при попытке клиента получить последовательность простых чисел, указав начальное число, которое не является простым. Исключение определяет новое свойство, NonPrimeкоторое возвращает неисчислимое число, вызвавшее исключение. Помимо реализации защищенного конструктора без параметров и конструктора с параметрами SerializationInfo и StreamingContext для сериализации, класс NotPrimeException определяет три дополнительных конструктора для поддержки свойства NonPrime. Каждый конструктор вызывает конструктор базового класса, а также сохраняет значение непростого числа. Класс NotPrimeException также помечается атрибутом SerializableAttribute .

C#
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; } }
}

Класс, показанный в следующем примере, использует решето Эратосфена для вычисления последовательности простых чисел от 2 до предела, указанного клиентом в вызове конструктора класса. Метод GetPrimesFrom возвращает все простые числа, превышающие или равные указанному нижнему пределу, но генерирует NotPrimeException, если этот нижний предел не является простым числом.

C#
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();
   }
}

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

C#
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("--------");
        }
    }
}

Примеры

В следующем примере показан блок catch (with в F#), определенный для обработки ошибок ArithmeticException. Этот catch блок также перехватывает DivideByZeroException ошибки, потому что DivideByZeroException происходит из ArithmeticException, и блок для DivideByZeroException ошибок явно не определен.

C#
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()

*/