1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 58
Текст из файла (страница 58)
Наследование315Эта программа отображает значение 10. Обратите внимание на то, что значениепеременной x приводится к объектному типу простым ее присваиванием переменной obj,которая является ссылкой на объект типа object. Целочисленное значение, хранимое вссылочной переменной obj, извлекается с помощью операции приведения к типу int.А вот еще один (более интересный) пример приведения значения к объектному типу.На этот раз int-значение передается в качестве аргумента методу sqr(), которыйиспользует параметр типа object.// Приведение значений к объектному типу возможно// при передаче значений методам.using System;class BoxingDemo {public static void Main() {int x;x = 10;Console.WriteLine("Значение x равно: " + x);}}// Переменная x автоматически приводится// к объектному типу при передаче методу sqr().x = BoxingDemo.sqr(x);Console.WriteLine("Значение x в квадрате равно: " + x);static int sqr(object o) {return(int)o * (int)o;}При выполнении этой программы получаются такие результаты:Значение x равно: 10Значение x в квадрате равно: 100Здесь значение x при передаче методу sqr() автоматически приводится кобъектному типу.Приведение к объектному типу и обратный процесс делает C#-систему типовполностью унифицированной.
Все типы выводятся из класса object. Ссылке типаobject можно присвоить ссылку на любой тип. В процессе приведения значения кобъектному типу и его восстановления из объекта автоматически обрабатываются вседетали, соответствующие нессылочным типам. Более того, поскольку все типы выведены изкласса object, все они имеют доступ к методам этого класса. Рассмотрим, например,следующую маленькую, но удивительную программу.// "Объективизация" позволяет вызывать методы// класса object для значений нессылочного типа!using System;class MethOnValue {}public static void Main() {Console.WriteLine(10.ToString());}316Часть I. Язык C#Эта программа отображает значение 10.
Дело в том, что метод ToString()возвращает строковое представление объекта, для которого он вызывается. В данном случаестроковое представление числа 10 выглядит как 10!Использование класса object в качестве обобщенного типа данныхС учетом того, что object — базовый класс для всех остальных C#-типов и чтоприведение значения к объектному типу и его восстановление из объекта происходятавтоматически, класс object можно использовать в качестве обобщенного типа данных.Например, рассмотрим следующую программу, которая создает массив объектов классаobject, а затем присваивает его элементам данные различных типов.// Использование класса object для создания// массива обобщенного типа.using System;class GenericDemo {public static void Main() {object[] ga = new object[10];// Сохраняем int-значения.for(int i=0; i < 3; i++)ga[i] = i;// Сохраняем double-значения.for(int i=3; i < 6; i++)ga[i] = (double) i / 2;// Сохраняем две строки, bool- и char-значения.ga[6] = "Массив обобщенного типа";ga[7] = true;ga[8] = 'X';ga[9] = "Конец";}}for(int i = 0; i < ga.Length; i++)Console.WriteLine("ga[" + i + "]: " + ga[i] + " ");Вот результаты выполнения этой программы:ga[0]: 0ga[1]: 1ga[2]: 2ga[3]: 1,5ga[4]: 2ga[5]: 2,5ga[6]: Массив обобщенного типаga[7]: Truega[8]: Xga[9]: КонецКак видно по результатам выполнения программы, ссылку на объект классаobject можно использовать в качестве ссылки на данные других типов.
Таким образом,Глава 11. Наследование317массив типа object в этой программе может хранить значения любого типа. Это говорит отом, что object-массив по сути представляет собой обобщенный список. Развивая этуидею, нетрудно создать класс стека, который бы хранил ссылки на объекты класса object.Это позволило бы стеку сохранять данные любого типа.Несмотря на эффективность такого обобщенного типа, как класс object, внекоторых ситуациях, было бы ошибкой думать, что класс object следует использоватькак способ обойти строгий C#-контроль соответствия типов. Другими словами, дляхранения int-значения используйте int-переменную, для хранения string-значения —string-ссылку и т.д. Оставьте обобщенную природу класса object для особых ситуаций.318Часть I.
Язык C#Полныйсправочник поГлава 12Интерфейсы, структуры иперечисленияЭта глава посвящена одному из самых важных средств языка C#: интерфейсу.Интерфейс определяет набор методов, которые будут реализованы классом. Саминтерфейс не реализует методы. Таким образом, интерфейс — это логическая конструкция,которая описывает методы, не устанавливая жестко способ их реализации. В этой главерассматриваются еще два типа данных C#: структуры и перечисления. Структуры подобныклассам, за исключением того, что они обрабатываются как типы значений, а не какссылочные типы.
Перечисления — это списки именованных целочисленных констант.Структуры и перечисления вносят существенный вклад в общую копилку средств иинструментов, составляющих среду программирования C#.ИнтерфейсыВ объектно-ориентированном программировании иногда требуется определить, чтокласс должен делать, а не как он будет это делать. Вы уже видели такой подход на примереабстрактного метода.
Абстрактный метод определяет сигнатуру для метода, но необеспечивает его реализации. В производном классе каждый абстрактный метод,определенный базовым классом, реализуется по-своему. Таким образом, абстрактный методзадает интерфейс для метода, но не способ его реализации. Несмотря на всю полезностьабстрактных классов и методов, эту идею можно развить.
В C# предусмотрена возможностьполностью отделить интерфейс класса от его реализации с помощью ключевого словаinterface.Интерфейсы синтаксически подобны абстрактным классам. Однако в интерфейсе ниодин метод не может включать тело, т.е. интерфейс в принципе не предусматривает какойбы то ни было реализации. Он определяет, что должно быть сделано, но не уточняет, как.Коль скоро интерфейс определен, его может реализовать любое количество классов.
Приэтом один класс может реализовать любое число интерфейсов.Для реализации интерфейса класс должен обеспечить тела (способы реализации)методов, описанных в интерфейсе. Каждый класс может определить собственнуюреализацию. Таким образом, два класса могут реализовать один и тот же интерфейсразличными способами, но все классы поддерживают одинаковый набор методов.Следовательно, код, “осведомленный” о наличии интерфейса, может использовать объектылюбого класса, поскольку интерфейс для всех объектов одинаков. Предоставляяпрограммистам возможность применения такого средства программирования, какинтерфейс, C# позволяет в полной мере использовать аспект полиморфизма, выражаемыйкак “один интерфейс — много методов”.Интерфейсы объявляются с помощью ключевого слова interface. Вот каквыглядит упрошенная форма объявления интерфейса:interface имя{тип_возврата имя_метода1(список_параметров);тип_возврата имя_метода2(список_параметров);// ...тип_возврата имя_методаN(список_параметров);}Имя интерфейса задается элементом имя.
Методы объявляются с использованиемлишь типа возвращаемого ими значения и сигнатуры. Все эти методы, по сути, —абстрактные. Как упоминалось выше, для методов в интерфейсе не предусмотрены способыреализации. Следовательно, каждый класс, который включает интерфейс, долженреализовать все его методы.
В интерфейсе методы неявно являются открытыми (publicметодами), при этом не разрешается явным образом указывать спецификатор доступа.320Часть I. Язык C#Рассмотрим пример интерфейса для класса, который генерирует ряд чисел,public interface ISeries {int getNext(); // Возвращает следующее число ряда.void reset(); // Выполняет перезапуск.void setStart(int x);// Устанавливает начальное// значение.}Этот интерфейс имеет имя ISeries. Хотя префикс “I” необязателен, многиепрограммисты его используют, чтобы отличать интерфейсы от классов. ИнтерфейсISeries объявлен открытым, поэтому он может быть реализован любым классом в любойпрограмме.Помимо сигнатур методов интерфейсы могут объявлять сигнатуры свойств,индексаторов и событий. События рассматриваются в главе 15, поэтому здесь мыостановимся на методах, индексаторах и свойствах.
Интерфейсы не могут иметь членовданных. Они не могут определять конструкторы, деструкторы или операторные методы.Кроме того, ни один член интерфейса не может быть объявлен статическим.Реализация интерфейсовИтак, если интерфейс определен, один или несколько классов могут его реализовать.Чтобы реализовать интерфейс, нужно указать его имя после имени класса подобно тому,как при создании производного указывается базовый класс. Формат записи класса, которыйреализует интерфейс, таков:class имя_класса : имя_интерфейса {// тело класса}Нетрудно догадаться, что имя реализуемого интерфейса задается с помощьюэлемента имя_интерфейса.Если класс реализует интерфейс, он должен это сделать в полном объеме, т.е.реализация интерфейса не может быть выполнена частично.Классы могут реализовать несколько интерфейсов.
В этом случае имена интерфейсовотделяются запятыми. Класс может наследовать базовый класс и реализовать один илинесколько интерфейсов. В этом случае список интерфейсов должно возглавлять имябазового класса.Методы, которые реализуют интерфейс, должны быть объявлены открытыми. Дело втом, что методы внутри интерфейса неявно объявляются открытыми, поэтому ихреализации также должны быть открытыми. Кроме того, сигнатура типа в реализацииметода должна в точности совпадать с сигнатурой типа, заданной в определенииинтерфейса.Рассмотрим пример реализации интерфейса ISeries, объявление которогоприведено выше. Здесь создается класс с именем ByTwos, генерирующий ряд чисел, вкотором каждое следующее число больше предыдущего на два.// Реализация интерфейса ISeries.class ByTwos : ISeries {int start;int val;public ByTwos() {start = 0;val = 0;}public int getNext() {Глава 12.