Рис. 6.8. Отладка необработанных пользовательских исключений в Visual Studio 2005
Если щелкнуть на ссылке View Detail (Показать подробности), появится дополнительная информация о состоянии объекта (рис. 6.9).
Рис. 6.9. Подробности отладки необработанных пользовательских исключений в Visual Studio 2005
Замечание. Если вы не обработаете исключение, сгенерированное методом из библиотеки базовых классов .NET, отладчик Visual Studio 2005 остановит выполнение программы на том операторе, который вызвал метод, создающий проблемы.
Резюме
В этой главе мы обсудили роль структурированной обработки исключений. Когда методу требуется отправить объект ошибки вызывающей стороне, этот метод создает, конфигурирует и посылает специальный тип System.Exception, используя для этого ключевое слово C# throw. Вызывающая сторона может обрабатывать поступающие исключения с помощью конструкций, в которых используются ключевое слово catch и необязательный блок finally.
При создании пользовательских исключений вы создаете тип класса, производный от System.ApplicationException, что означает исключение, генерируемое выполняемым приложением. В противоположность этому объекты ошибок, получающиеся из System.SystemException представляют критические (и фатальные) ошибки, генерируемые средой CLR. Наконец, в этой главе были представлены различные инструменты Visual Studio 2005, которые можно использовать при отладке и при создании пользовательских исключений (в соответствии с лучшими образцами .NET).
ГЛАВА 7. Интерфейсы и коллекции
В этой главе предлагается рассмотреть тему программирования на основе интерфейсов, чтобы расширять ваши представления об объектно-ориентированном подходе в области разработки приложений. Здесь вы узнаете, как в рамках C# определяются и реализуются интерфейсы, и поймете, в чем заключаются преимущества типов, поддерживающих ''множественное поведение". В процессе обсуждения будет рассмотрен и ряд смежных вопросов – в частности, получение интерфейсных ссылок, явная реализация интерфейсов, а также иерархии интерфейсов.
Часть главы будет посвящена рассмотрению целого ряда интерфейсов, определенных в рамках библиотек базовых классов .NET. Вы увидите, что определенные вами пользовательские типы тоже можно встраивать в эти предопределенные интерфейсы, чтобы обеспечить поддержку специальных функций, таких, как клонирование, перечисление и сортировка объектов.
Чтобы продемонстрировать, как интерфейсы используются в библиотеках базовых классов .NET, в этой главе будут рассмотрено множество встроенных интерфейсов, реализуемых различными классами коллекций (ArrayList, Stack и т.п.), определенными в пространстве имен System.Collections. Информация, представленная здесь, будет необходима для понимания материала главы 10, в которой расcматриваются обобщения .NET и пространство имен Collections.Generiс.
Определение интерфейсов в C#
Изложение материала этой главы мы начнем с формального определения типа "интерфейс". Интерфейс – это просто именованная коллекция семантически связанных абстрактных членов. Эти члены определяются интерфейсом в зависимости от поведения, которое моделирует данный интерфейс. Интерфейс отражает поведение, которое может поддерживаться данным классом или структурой.
В рамках синтаксиса C# интерфейс определяется с помощью ключевого слова interfасе. В отличие от других типов .NET, интерфейсы никогда не указывают базовый класс (включая System.Object) и для их членов никогда не указываются модификаторы доступа (поскольку все члены интерфейса неявно считаются открытыми). Вот пример пользовательского интерфейса, определенного на языке C#.
// Этот интерфейс определяет наличие вершин.
public interface IPointy {
// Неявно открытый и абстрактный.
byte GetNumberOfPoints();
}
Замечание. По соглашению имена интерфейсов в библиотеках базовых классов .NET имеют префикс "I" (прописная буква "i" латинского алфавита). При создании пользовательского интерфейса рекомендуется придерживаться аналогичных правил.
Как видите, интерфейс IPointy определяет единственный метод. Однако типы интерфейса в .NET могут также определять любое число свойств. Например, можно определить интерфейс IPointy, в котором используется доступное только для чтения свойство вместо традиционного метода чтения данных.
// Реализация поведения в виде свойства, доступного только для чтения.
public interface IPointy {
byte Points {get;}
}
Вы должны понимать, что типы интерфейса сами по себе совершенно бесполезны, поскольку они представляют собой всего лишь именованные коллекции абстрактных членов. В этой связи вы не можете размещать типы интерфейса так, как это делается с классом или структурой.
// Создавать типы интерфейса с помощью "new" не допускается.
static void Main(string[] args) {
IPointy p = new IPointy(); // Ошибка компиляции!
}
Интерфейсы не приносят пользы, если они не реализованы некоторым классом или структурой. Здесь IPointy является интерфейсом, отражающим "наличие вершин". Такое поведение может быть полезным в иерархии форм, построенной нами в главе 4. Идея очень проста: некоторые классы в иерархии форм имеют вершины (например, Hexagon – шестиугольник), а другие (например, Circle – круг) вершин не имеют. Реализовав интерфейс IPointy в Hexagon и Triangle, вы можете предполагать, что оба класса поддерживают общий тип поведения, а поэтому и общее множество членов.
Реализация интерфейсов в C#
Чтобы расширить функциональные возможности класса (или структуры) путем поддержки типов интерфейса, нужно просто указать в определении класса (или структуры) список соответствующих типов, разделив их запятыми. Непосредственный базовый класс должен быть первым элементом в списке, следующим после операции, обозначаемой двоеточием. Когда тип класса получается непосредственно из System.Object, можно указать только список интерфейсов, поддерживаемым классом, поскольку при отсутствии явного указании компилятор C# получает типы именно из System.Object. Точно так же, поскольку структуры всегда получаются из System.ValueType (см. главу 3), можно указать только интерфейсы в списке, следующем непосредственно после определения структуры. Рассмотрите следующие примеры.
// Этот класс является производным System.Object.
// и реализует один интерфейс.
public сlаss SomeClass: ISomeInterface {…}
// Этот класс является производным System.Object
// и реализует один интерфейс.
public class MyClass: object, ISomeInterface {…}
// Этот класс является производным пользовательского базового класса
// и реализует один интерфейс.
public class AnotherClass: MyBaseClass, ISomeInterface {…}
// Эта структура является производной System.ValueType
// и реализует два интерфейса.
public struct SomeStruct: ISomeInterface, IPointy
Вы должны понимать, что реализация интерфейса подчиняется принципу "все или ничего". Тип, реализующий интерфейс, не может обеспечивать селективную поддержку членов этого интерфейса. Если интерфейс IPointy определяет единственное свойство, то для выбора вариантов нет. Однако если реализовать интерфейс, определяющий десять членов, то соответствующий тип будет обязан конкретизировать все десять абстрактных элементов.
Так или иначе, вот вам пример реализации обновленной иерархии форм (и обратите внимание на новый тип класса Triangle – треугольник).
// Hexagon теперь реализует IPointy.
public class Hexagon: Shape, IPointy {
public Hexagon() {}
public Hexagon(string name): base (name) {}
public override void Draw() { Console.WriteLine("Отображение шестиугольника {0} ", PetName); }
// Реализация IPointy.
public byte Points {
get { return 6; }
}
}
// Новый производный класс Triangle, полученный из Shape.
public class Triangle: Shape, IPointy {
public Triangle() {}
public Triangle(string name): base(name) {}
public override void Draw() { Console.WriteLine("Отображение треугольника {0} ", PetName); }
// Реализация IPointy.
public byte Points {
get { return 3; }
}
}
Теперь каждый класс при необходимости возвратит вызывающей стороне число вершин. Чтобы резюмировать сказанное, рассмотрите диаграмму на рис. 7.1, которая была получена в Visual Studio 2005 и иллюстрирует совместимые по интерфейсу IPointy классы, используя популярное обозначение интерфейса знаком "леденца на палочке".
Рис. 7.1. Иерархия форм (теперь с интерфейсами)