Студопедия — Необходимые теоретические сведения. Интерфейс — это набор семантически связанных абстрактных членов
Студопедия Главная Случайная страница Обратная связь

Разделы: Автомобили Астрономия Биология География Дом и сад Другие языки Другое Информатика История Культура Литература Логика Математика Медицина Металлургия Механика Образование Охрана труда Педагогика Политика Право Психология Религия Риторика Социология Спорт Строительство Технология Туризм Физика Философия Финансы Химия Черчение Экология Экономика Электроника

Необходимые теоретические сведения. Интерфейс — это набор семантически связанных абстрактных членов






Интерфейс

Интерфейс — это набор семантически связанных абстрактных членов. Количество членов, определенных в конкретном интерфейсе, зависит от того, какое поведение мы пытаемся смоделировать при помощи этого интерфейса. С точки зрения синтаксиса интерфейсы в С# определяются следующим образом:

// Этот интерфейс определяет возможности работы с вершинами
// геометрической фигуры

public interface IPointy

{

byte GetNumberOfPoints(); //автоматически этот член

// интерфейса становится абстрактным

}

Интерфейсы.NET также могут поддерживать любое количество свойств (и событий). Например, интерфейс IPointy может вместо метода содержать такое свойство для чтения:

public interface IPointy

{

// Чтобы сделать это свойство свойством "только для чтения" или "только

// для записи", достаточно просто удалить соответствующий блок set или get

byte Points { get;}

}

В любом случае интерфейс — это не более чем именованный набор абстрактных членов, а это значит, что любой класс, реализующий этот интерфейс, должен самостоятельно полностью определять каждый из членов этого интерфейса. Таким образом, интерфейсы — это еще один способ реализации полиморфизма в приложении: поскольку в разных классах члены одних и тех же интерфейсов будут реализованы по-разному, в результате эти классы будут реагировать на одни и те же вызовы по-своему.

В качестве примера мы будем использовать иерархию геометрических фигур, производных от базового класса Shape. Наш интерфейс IPointy — это элементарный интерфейс, возвращающий количество углов у геометрической фигуры.

К этому моменту у вас мог созреть один интересный вопрос. Как мы уже выяснили, интерфейс — это набор абстрактных членов. Однако С# позволяет нам использовать абстрактные члены и в обычном классе. Так зачем же вообще нужны интерфейсы, если мы можем создать набор абстрактных методов, реализовав его как класс, и сделать этот класс базовым для наших пользовательских классов?

Ответ будет прост: класс — это класс, а интерфейс — это интерфейс. В классах помимо абстрактных методов, свойств и событий определяются также переменные класса и обычные (не абстрактные) методы, появление которых в интерфейсе исключено.

Интерфейс — это чистая синтаксическая конструкция, которая предназначена только для определенных целей. Интерфейсы никогда не являются типами данных, и в них не бывает реализаций методов по умолчанию. Каждый член интерфейса (будь то свойство или метод) автоматически становится абстрактным. Кроме того, в С# наследование одного класса более чем от одного базового класса (то есть множественное наследование) запрещено. В то же время реализация в классе сразу нескольких интерфейсов — это обычное дело.

Реализация интерфейса

Когда в С# какой-либо класс должен реализовать нужные нам интерфейсы, названия этих интерфейсов должны быть помещены (через запятую) после двоеточия, следующего за именем этого класса. Обратите внимание, что имя базового класса всегда должно стоять перед именами любых интерфейсов:

// Любой класс может реализовывать любое количество интерфейсов, но он
// имеет только один базовый класс:

public class Hexagon: Shape, IPointy

{

public Hexagon() { }

public byte Points

{

get { return 6; }

}

}

public class Triangle: Shape, IPointy

{

public Triangle() { }

public byte Points

{

get {return 3;}

}

}

Теперь и Hexagon, и Triangle, когда их об этом попросят, предоставят информацию о том, сколько у них углов. Обратите внимание, что вы не можете выбирать, какие методы в интерфейсе вам реализовывать, а какие — нет. В классе либо должны быть реализованы все методы данного интерфейса, либо этот интерфейс вообще не реализуется — половинчатого решения быть не может.

 

Получение ссылки на интерфейс

С# позволяет обращаться к членам интерфейсов несколькими способами. Предположим, что мы создаем объект одного из наших классов и нам необходимо узнать, сколько у него углов.

Первый способ — воспользоваться явным приведением типов:

// Получаем ссылку на интерфейс IPointy, используя явное приведение типов

Hexagon hex = new Hexagon(“Bill”);

IPointy itfPt = (IPointy)hex;

Console.WriteLine(itfPt.Points);

Здесь мы получаем ссылку на интерфейс IPointy, явно приводя объект класса Hexagon к типу IPointy. Если класс Hexagon поддерживает интерфейс IPointy, мы получим ссылку на интерфейс (у нас она называется itfPt) и все будет хорошо. Однако, когда мы пытаемся получить ссылку на интерфейс путем явного приведения типов для объекта класса, не поддерживающего данный интерфейс, система генерирует исключение InvalidCastException. Чтобы избежать проблем с исключением, исключение нужно перехватить:

// Используя программные средства, поэтапно перехватываем исключение

Circle с = new Circle(“Lisa”);

IPointy itfPt;

try {

itfPt = (IPointy)c;

Console.WriteLine(itfPt.Points());

}

catch (InvalidCastException e)

{ Console.WriteLine(“OPS! Not pointy..."); }

Второй способ получить ссылку на интерфейс — использовать ключевое слово as:

// Еще один способ получить ссылку на интерфейс

Hexagon hex2 = new Hexagon(“Peter”);

IPointy itfPt2:

itfPt2 = hex2 as IPointy;

if (itfPt2!= null)

Console.WriteLine(itfPt2.Points();

else

Console.Wr1teLine("OOPS! Not pointy...");

Если при использовании ключевого слова as мы попробуем создать ссылку на интерфейс через объект, который этот интерфейс не поддерживает, ссылка будет просто установлена в null, и при этом никаких исключений генерировать- ся не будет.

Третий способ получения ссылки на интерфейс — воспользоваться оператором is. Если объект не поддерживает интерфейс, условие станет равно false:

// Есть ли у тебя углы?

Triangle t = new Triangle();

if(t is IPointy)

Console.WriteLine(t.Points());

else

Console.WriteLine(“OOPS! Not pointy...”);

Если у нас имеется массив разных объектов и нам необходимо будет выяснить в процессе выполнения, какие именно объекты из этого массива поддерживают определенный интерфейс, это можно сделать любым из приведенных выше способов.

Создание иерархий интерфейсов

В С# один интерфейс может наследовать другому. Как обычно, базовый интерфейс определяет общее поведение, в то время как производный интерфейс — более конкретное и специфическое. Простая иерархия интерфейсов может выглядеть следующим образом:

// Базовый интерфейс

interface IDrawable

{

void Draw();

}

interface IPrintable: IDrawable

{

void Print();

}

interface IMetaFileRender: IPrintable

{

void Render();

}

Если наш класс должен поддерживать поведение, определенное во всех трех интерфейсах, то базовым для него должен быть интерфейс самого нижнего уровня (в нашем случае — IMetaFileRender). Все методы, определенные в интерфейсах более высокого уровня, будут автоматически включены в производные интерфейсы.

Наследование от нескольких базовых интерфейсов

С# допускает наследование сразу от нескольких базовых интерфейсов. При этом еще раз заметим, что множественное наследование между классами (когда один класс является производным одновременно от нескольких классов) в С# запрещено — множественное наследование разрешается только для интерфейсов. Проиллюстрируем возможности множественного наследования интерфейсов на примере.

Предположим, что в нашем распоряжении есть набор интерфейсов, моде- лирующих поведение автомобиля:

interface ICar

{

void Drive(); // ехать

}

interface IUnderwaterCar

{ void Dive(); } // нырять

// Этот интерфейс имеет ДВА базовых.

interface IJamesBondCar: ICar, IUnderwaterCar

{

void TurboBoost(); // разгоняться

}

Если мы захотим создать класс, который реализует интерфейс IJamesBondCar, нам придется реализовать в нем все методы каждого из интерфейсов.

public class JamesBondCar: IJamesBondCar

{

public void Drive() { Console.WriteLine("Speeding up..."); }

public void Dive() { Console.WriteLine("Submerging..."); }

public void TurboBoost() { Console.WriteLine("Blast off!"); }

}

Стандартные интерфейсы

В библиотеку базовых классов.Net встроено большое количество стандартных интерфейсов. Для создания сравниваемых объектов используется

интерфейс IComparable. Если мы реализуем этот стандартный интерфейс для своего класса, то сможем воспользоваться встроенным методом сортировки для массива объектов своего класса.

Формальное определение интерфейса выглядит следующим образом:

// Этот интерфейс позволяет определять место объекта среди других
// аналогичных объектов

interface IComparable

{

int CompareTo(object о);

}

Поскольку этот интерфейс состоит из единственного метода CompareTo(), вся соль заключается в том, как будет реализован этот метод. Прежде всего мы должны определить, по значению какой внутренней переменной будет производиться сортировка. Для типа Employee, описанного в первой лабораторной работе, самая подходящая переменная — это табельный номер.

// Такая реализация метода CompareТо() позволит сортировать объекты
// работников по значению табельного номера - empID

public class Employee: IComparable

{

private string fullName; // Имя сотрудника

private int empID; // табельный номер

...

// Реализация IComparable

int IComparable. CompareTo (object o)

{

Employee temp = (Employee)o;

if(this.empID > temp. emplD)

return 1;

if((this.empID < temp. emplD)

return -1;

else return 0;

}

}

Как видно из этого кода, метод CompareTo() сравнивает значение empID для текущего объекта (того, для которого вызван этот метод) со значением empID для принимаемого объекта (того объекта, который передан этому методу в качестве входящего параметра). В зависимости от результатов сравнения выдается одно из трех возможных значений. Заметим, что может возвращаться любое число больше нуля, если значение переменной для текущего объекта больше, чем у принимаемого.

Теперь мы можем спокойно производить сортировку массива объектов.

static void Main()

{

Employee[] otdel = new Employee[4];

otdel[0] = new Manager("Chuckiy H.", 1020, 19000.0f, 34);

otdel[1] = new Manager(“Petrov Petr A.”, 1002, 14000.0f, 40);

otdel[2] = new SalesPerson(“Ivanov A.”, 2101, 16500.0f, 2400);

otdel[3] = new SalesPerson(“Elin Al.”, 2002, 13300.0f, 3600);

// используем возможности IComparable

Array.Sort(otdel);

...

}

Кроме IComparable, на практике наиболее часто используются стандартные интерфейсы IEnumerable – для создания перечислимых объектов, ICloneable – для глубокого копирования объектов. Узнать подробности об их реализации вы можете на сайте MSDN или в книге[1].

Коллекции

Платформа.NET Framework предоставляет кроме массивов еще и специализированные классы для хранения и извлечения данных. Эти классы обеспечивают поддержку стеков, очередей, списков и хэш-таблиц. Большинство классов коллекций реализуют одинаковые интерфейсы, и эти интерфейсы могут наследоваться для создания новых классов коллекций, соответствующим более специализированным потребностям в хранении данных. Классы коллекций заданы как часть пространства имен System.Collections. Большинство классов коллекций являются производными от интерфейсов ICollection,IComparer,IEnumerable,IList,IDictionaryиIDictionaryEnumerator.

Наиболее часто на практике используются следующие классы коллекций:

ArrayList Динамически изменяющий свой размер массив объектов.
Hashtable   Представляет набор взаимосвязанных ключей и значений, основанных на хэш-коде ключа.
Queue Стандартная очередь, реализованная по принципу FIFO (first-in-first-out, «первым пришел, первым ушел»).
Sorted List   Похож на словарь, однако к элементам можно также обратиться по их порядковому номеру(индексу).
Stack Стек, реализованный по принципу LIFO (last-in-first-out, «последним пришел, первым ушел»), обеспечивающий возможности по проталкиванию данных в стек, выталкиванию данных из стека и считыванию данных.

Динамический массив ArrayList очень удобен в применении, так как позволяет включать произвольное число элементов любого типа, так как хранит все элементы, как объекты наиболее общего типа System.Object. Но при этом ArrayList не обеспечивает типовую безопасность. Если по ошибке в коллекцию элементов одного типа мы добавим экземпляр другого, то ошибка будет обнаружена только во время выполнения, при попытке вызвать несуществующий метод. Выходом в этом случае является использование обобщенных коллекций.

Обобщенные коллекции

Обобщенные классы коллекций обеспечивают повышенную безопасность типа. Их описание содержится в пространстве имен System.Collections.Generic. Это пространство имен содержит множество классов и интерфейсов, позволяющих помещать элементы в различные контейнеры, а именно:

List<T>   Список элементов типа Т с динамически изменяемым размером.
Dictionary <K,V> Обобщенная коллекция пар имя/значение.
Queue<T> Обобщенная очередь элементов типа T.
SortedDictionary <K,V> Обобщенная реализация сортированного множества пар имя/значение.
Stack<T> Обобщенный стек элементов типа Т.
LinkedList<T> Обобщенная реализация двусвязного списка.

Обобщенные классы являются объектами, память для которых выделяется в куче, поэтому они должны создаваться с помощью оператора new с необходимыми аргументами конструктора. Кроме того, для них нужно указать тип(типы), который будет подставлен в параметр(параметры) обобщенного класса. Поэтому если мы хотим создать список целочисленных значений и список объектов класса Employee, то соответствующий код будет выглядеть так:

static void Main(string[] args)

{

// создаем список, содержащий целочисленные значения

List<int> myInts = new List<int>();

// помещаем в список элементы

myInts.Add(5);

int x = 10;

myInts.Add(x);

// создаем список, содержащий объекты Employee

List<Employee> otdel = new List<Employee>();

otdel.Add(new Manager());

otdel.Add(new SalesPerson());

Console.Writeline(“В отделе работает {0} людей”, otdel.count);

}

Мы можем создавать собственные обобщенные методы, структуры, классы, интерфейсы. Ключевое слово where, стоящее после описания обобщенного метода(класса, интерфейса), задает ограничение на параметр типа Т. Т может быть только значимым типом, или иметь в качестве базового какой-то определенный класс. Подробнее о возможных значениях для ограничений вы можете посмотреть в [1]. Приведем несколько примеров.

// Этот метод осуществляет обмен значений двух значимых типов

public static void Swap<T>(ref T a, ref T b) where T: struct

{

Console.WriteLine("You sent the Swap() method a {0}", typeof(T));

T temp;

temp = a;

a = b;

b = temp;

}

Ниже приведен пример обобщенной структуры.

public struct Point<T>

{

// обобщенные поля данных

private T xPos;

private T yPos;

// конструктор

public Point(T xVal, T yVal)

{

xPos = xVal;

yPos = yVal;

}

// Обобщенные свойства

public T X

{

get { return xPos; }

set { xPos = value; }

}

public T Y

{

get { return yPos; }

set { yPos = value; }

}

public override string ToString()

{

return string.Format("[{0}, {1}]", xPos, yPos);

}

}

Наконец рассмотрим пример обобщенного интерфейса и класса, который этот интерфейс реализует.

public interface IBinaryOperations<T>

{

T Add(T arg1, T arg2);

T Subtract(T arg1, T arg2);

T Multiply(T arg1, T arg2);

T Divide(T arg1, T arg2);

}

public class BasicMath: IBinaryOperations<int>

{

public BasicMath() {}

// IBinaryOperations<int> Members

 

public int Add(int arg1, int arg2)

{ return arg1 + arg2; }

public int Subtract(int arg1, int arg2)

{ return arg1 - arg2; }

public int Multiply(int arg1, int arg2)

{ return arg1 * arg2; }

public int Divide(int arg1, int arg2)

{ return arg1 / arg2; }

}

Контрольные вопросы

1) Дайте определение интерфейса. Для каких целей применяются интерфейсы?

2) Как можно получить ссылку на интерфейс? Приведите примеры.

3) В чем особенность иерархий интерфейсов?

4) Какие стандартные интерфейсы вы знаете? Приведите примеры их реализации.

5) Объясните сходства и различия классов коллекций и обобщенных коллекций.

6) Приведите примеры обобщенных структур данных.

7) Чем отличается синтаксис интерфейса от синтаксиса абстрактного

класса?

8) Какие объекты языка C# могут быть членами интерфейсов?

9) Каким количеством классов может быть реализован интерфейс?

10) Может ли класс реализовывать множественные интерфейсы?

11) Какой модификатор доступа соответствует интерфейсу?

12) Приведите синтаксис интерфейса в общем виде. Проиллюстрируйте его фрагментом программы на языке C#.

13) Возможно ли наследование интерфейсов?

14) Насколько синтаксис наследования интерфейсов отличается от

синтаксиса наследования классов?







Дата добавления: 2015-09-06; просмотров: 727. Нарушение авторских прав; Мы поможем в написании вашей работы!



Обзор компонентов Multisim Компоненты – это основа любой схемы, это все элементы, из которых она состоит. Multisim оперирует с двумя категориями...

Композиция из абстрактных геометрических фигур Данная композиция состоит из линий, штриховки, абстрактных геометрических форм...

Важнейшие способы обработки и анализа рядов динамики Не во всех случаях эмпирические данные рядов динамики позволяют определить тенденцию изменения явления во времени...

ТЕОРЕТИЧЕСКАЯ МЕХАНИКА Статика является частью теоретической механики, изучающей условия, при ко­торых тело находится под действием заданной системы сил...

Условия приобретения статуса индивидуального предпринимателя. В соответствии с п. 1 ст. 23 ГК РФ гражданин вправе заниматься предпринимательской деятельностью без образования юридического лица с момента государственной регистрации в качестве индивидуального предпринимателя. Каковы же условия такой регистрации и...

Седалищно-прямокишечная ямка Седалищно-прямокишечная (анальная) ямка, fossa ischiorectalis (ischioanalis) – это парное углубление в области промежности, находящееся по бокам от конечного отдела прямой кишки и седалищных бугров, заполненное жировой клетчаткой, сосудами, нервами и...

Основные структурные физиотерапевтические подразделения Физиотерапевтическое подразделение является одним из структурных подразделений лечебно-профилактического учреждения, которое предназначено для оказания физиотерапевтической помощи...

Трамадол (Маброн, Плазадол, Трамал, Трамалин) Групповая принадлежность · Наркотический анальгетик со смешанным механизмом действия, агонист опиоидных рецепторов...

Мелоксикам (Мовалис) Групповая принадлежность · Нестероидное противовоспалительное средство, преимущественно селективный обратимый ингибитор циклооксигеназы (ЦОГ-2)...

Менадиона натрия бисульфит (Викасол) Групповая принадлежность •Синтетический аналог витамина K, жирорастворимый, коагулянт...

Studopedia.info - Студопедия - 2014-2024 год . (0.011 сек.) русская версия | украинская версия