Еще одно преимущество исключений .NET состоит в том, что в отличие от загадочных числовых значений они представляют собой объекты, в которых содержится читабельное описание проблемы, а также детальный снимок стека вызовов на момент первоначального возникновения исключения. Более того, конечному пользователю можно предоставить справочную ссылку, которая указывает на URL-адрес с подробностями об ошибке, а также специальные данные, определенные программистом.
Строительные блоки обработки исключений в .NET
Программирование со структурированной обработкой исключений предусматривает применение четырех взаимосвязанных сущностей:
• тип класса, который представляет детали исключения;
• член, способный генерировать экземпляр класса исключения в вызывающем коде при соответствующих обстоятельствах;
• блок кода на вызывающей стороне, который обращается к члену, предрасположенному к возникновению исключения;
• блок кода на вызывающей стороне, который будет обрабатывать (или перехватывать) исключение, если оно возникнет.
Язык программирования C# предлагает пять ключевых слов (try, catch, throw, finally и when), которые позволяют генерировать и обрабатывать исключения. Объект, представляющий текущую проблему, относится к классу, который расширяет класс System.Exception (или производный от него класс). С учетом сказанного давайте исследуем роль данного базового класса, касающегося исключений.
Базовый класс System.Exception
Все исключения в конечном итоге происходят от базового класса System.Exception, который в свою очередь является производным от System.Object. Ниже показана основная часть этого класса (обратите внимание, что некоторые его члены являются виртуальными и, следовательно, могут быть переопределены в производных классах):
public class Exception : ISerializable
{
// Открытые конструкторы
public Exception(string message, Exception innerException);
public Exception(string message);
public Exception();
...
// Методы
public virtual Exception GetBaseException();
public virtual void GetObjectData(SerializationInfo info,
StreamingContext context);
// Свойства
public virtual IDictionary Data { get; }
public virtual string HelpLink { get; set; }
public int HResult {get;set;}
public Exception InnerException { get; }
public virtual string Message { get; }
public virtual string Source { get; set; }
public virtual string StackTrace { get; }
public MethodBase TargetSite { get; }
}
Как видите, многие свойства, определенные в классе System.Exception, по своей природе допускают только чтение. Причина в том, что стандартные значения для каждого из них обычно будут предоставляться производными типами. Например, стандартное сообщение типа IndexOutOfRangeException выглядит так: "Index was outside the bounds of the array" (Индекс вышел за границы массива).
В табл. 7.1 описаны наиболее важные члены класса System.Exception.
Простейший пример
Чтобы продемонстрировать полезность структурированной обработки исключений, мы должны создать класс, который будет генерировать исключение в надлежащих (или, можно сказать, SimpleException и определим в нем два класса (Car (автомобиль) и Radio (радиоприемник)), связав их между собой отношением "имеет". В классе Radio определен единственный метод, который отвечает за включение и выключение радиоприемника:
using System;
namespace SimpleException
{
class Radio
{
public void TurnOn(bool on)
{
Console.WriteLine(on ? "Jamming..." : "Quiet time...");
}
}
}
В дополнение к использованию класса Radio через включение/делегацию класс Car (его код показан ниже) определен так, что если пользователь превышает предопределенную максимальную скорость (заданную с помощью константного члена MaxSpeed), тогда двигатель выходит из строя, приводя объект Car в нерабочее состояние (отражается закрытой переменной-членом типа bool по имени _carIsDead).