
Лекция 8(интрфКолл).ppt
- Количество слайдов: 58
Интерфейсы В С# предусмотрена возможность полностью отделить интерфейс класса от его реализации с помощью ключевого слова interface. Интерфейсы синтаксически подобны абстрактным классам. Однако в интерфейсе ни один метод не может включать тело. Предоставляя программистам возможность применения такого средства программирования, как интерфейс, С# позволяет в полной мере использовать аспект полиморфизма, выражаемый как "один интерфейс — много методов".
Объявление интерфейса Упрощенная форма объявления интерфейса: interface имя{ тип_возврата имя_метода 1 {список_параметров) ; тип_возврата имя_метода 2 {список_параметров) ; //. . . тип_возврата имя_метода. N(список_параметров) ; } Помимо сигнатур методов интерфейсы могут объявлять сигнатуры свойств, индексаторов и событий. Интерфейсы не могут иметь членов данных. Они не могут определять конструкторы, деструкторы или операторные методы. Кроме того, ни один член интерфейса не может быть объявлен статическим.
Пример интерфейса public interface ISeries { int get. Next(); // Возвращает следующее число ряда, void reset (); // Выполняет перезапуск, void set. Start(int x) ; // Уст. начальное // значение. } -----------------Хотя префикс " I " необязателен, его используют, чтобы отличать интерфейсы от классов.
Реализация интерфейсов Если интерфейс определен, один или несколько классов могут его реализовать. Чтобы реализовать интерфейс, нужно указать его имя после имени класса Формат записи класса, который реализует интерфейс: class имя_класса : имя_интерфейса { // тело класса }
Условия реализации Классы могут реализовать несколько интерфейсов. В этом случае имена интерфейсов отделяются запятыми. Класс может наследовать базовый класс и реализовать один или несколько интерфейсов. В этом случае список интерфейсов должно возглавлять имя базового класса. Методы, которые реализуют интерфейс, должны быть объявлены открытыми. Кроме того, сигнатура типа в реализации метода должна в точности совпадать с сигнатурой типа, заданной в определении интерфейса.
Интерфейс ISeries public interface ISeries { int get. Next(); // Возвращает следующее число ряда, void reset (); // Выполняет перезапуск, void set. Start(int x) ; // Уст. начальное // значение. }
Пример реализации интерфейса ISeries // Реализация интерфейса ISeries, class By. Twos : ISeries { int start; int val; public By. Twos() { start = 0; val = 0; } public int get. Next() { val += 2; return val; } public void reset() { val = start; } public void set. Start(int x) { start = x; val = start; } }
Использование класса By. Twos using System; class Series. Demo { public static void Main() { By. Twos ob = new By. Twos() ; for(int i=0; i < 5; i++) Console. Write. Line("Значение равно " + ob. get. Next()); Console. Write. Line("Переход в исходное состояние. ") ob. reset (); for(int i=0; i < 5; i++) Console. Write. Line(“Значение равно " +ob. get. Next()); Console. Write. Line("Начинаем с числа 100. "); ob. set. Start (100); for (int i=0; i < 5; i++) Console. Write. Line(" Значение равно " +“ ob. get. Next()); }}
Резюме • Интерфейс подобен абстрактному базовому классу: любой неабстрактный тип, наследующий интерфейс, должен реализовать все его члены. • Невозможно создать экземпляр интерфейса напрямую. • Интерфейсы могут содержать методы, свойства, индексаторы и события в качестве членов. • Интерфейсы не содержат реализации методов. • Как классы, так и структуры способны наследовать от нескольких интерфейсов. • Интерфейс может быть унаследован от нескольких интерфейсов.
Рекомендации В программировании на С# при необходимости описать функции, а не способ их реализации, важно знать, когда следует использовать интерфейс, а когда — абстрактный класс. Если вы полностью описываете действия класса и не нужно уточнять, как он это делает, следует использовать интерфейс. Если же требуется включить в описание детали реализации, имеет смысл представить концепцию программы (или ее части) в виде абстрактного класса.
Коллекции в. NET Framework Class Library System. Collections or System. Collections. Generic На каком бы языке вы не программировали, вы неизбежно столкнетесь с проблемой хранения и обработки наборов данных. От выбора структур хранения данных и алгоритмов в значительной степени зависит производительность, да и качество создаваемого вами ПО. Поэтому знание стандартных коллекций, а также умение грамотно и по месту применить их, дает возможность писать более быстрые и надежные программы. Коллекцией в дальнейшем будет называться некоторая конечная совокупность объектов, с которыми можно совершать те или иные действия.
Виды коллекций, включенные в FCL Тип Назначение Встроенные массивы Обычные массивы, поддерживаемые CLR напрямую. Array. List Является реализацией абстракции списка на базе массива, По сути, динамический массив, позволяющий хранить ссылки на объекты. Hashtable Реализует абстракцию «словарь» (Dictionary, коллекцию пар «ключ-значение» ) на основе алгоритма хэш-таблицы. Sorted. List Реализация абстракции словаря и списка на базе сортированного массива. Stack Реализует абстракцию «стек» Queue Реализует абстракцию «очередь» Bit. Array Позволяет управлять битовыми массивами
Специализированные коллекции Большинство коллекций из пространства имен System. Collections. Specialized специфичны, и редко используются на практике. Исключение составляют • Name. Object. Collection. Base, • String. Collection • String. Dictionary. Name. Object. Collection. Base удобна для создания собственных типизированных словарей, а String. Collection и String. Dictionary – готовые строковые коллекции.
Интерфейсы, используемые коллекциями. Название Описание IEnumerable Предоставляет итератор, который поддерживает простой перебор элементов коллекции. ICollection Определяет методы, позволяющие определить количество элементов в коллекции. IList Представляет интерфейс коллекции объектов, каждый из которых может быть получен по индексу. Также определяет методы модификации коллекции. IDictionary Представляет интерфейс коллекции пар «ключзначение» .
Интерфейс IEnumerable • Все коллекции в FCL реализуют интерфейс IEnumerable. Этот интерфейс позволяет перебрать элементы коллекции в цикле. • Интерфейс описывает всего один метод: IEnumerator Get. Enumerator(); Этот метод возвращает ссылку на интерфейс IEnumerator (перечислитель), при помощи которого можно осуществить перебор всех элементов коллекции. • Для одного экземпляра коллекции можно одновременно запросить несколько перечислителей. Поэтому такого понятия, как «текущий элемент» , нет
Набор дополнительных интерфейсов IComparer Определяет метод, осуществляющий сравнение двух объектов. IEnumerator Определяет методы, позволяющие осуществить простой перебор элементов коллекции. IComparable Используется при поиске и сортировке объектов. Может быть реализован типами, для которых определены операции сравнения. IDictionary. Enumerator Позволяет перебратьэлементы словаря. IHash. Code. Provider Определяет метод, позволяющий вычислить хэш-код для объекта.
Интерфейс IEnumerator Предназначен для перебора значений коллекции. В состав этого интерфейса входят: bool Move. Next() void Reset() Object Current { get; } -----------------Enumerator enumerator = ((IEnumerable)some. Collection). Get. Enumerator(); while (enumerator. Move. Next()) { Elem. Type elem = (Elem. Type)enumerator. Current(); //. . . какие-то действия с элементом коллекции. }
Продолжение Использовать связку IEnumerable/IEnumerator удобнее всего с помощью оператора foreach. Так, приведенный выше пример можно переписать следующим образом: foreach (Elem. Type elem in some. Collection) { //. . . какие-то действия с элементом коллекции. }
Интерфейс ICollection public interface ICollection : IEnumerable int Count {get; } свойство, при помощи которого можно получить число элементов коллекции void Copy. To(Array array, int index); Копирование элементов коллекции в массив. Object Sync. Root { get; } возвращает ссылку на объект, который должен использоваться для синхронизации доступа к объекту.
Интерфейс IList public interface IList : ICollection, IEnumerable В интерфейсе IList впервые встречается способ получить или присвоить какое-то значение элементу коллекции. Это делается с помощью индексатора: object this[int index] { get; set; } Добавление и вставка int Add(object value); void Insert(int index, object value); Удаление void Remove(object value); void Remove. At(int index); void Clear(); Проверка bool Contains(object value); int Index. Of(object value);
Интерфейс IDictionary Свойства Свойство Is. Fixed. Size Is. Read. Only Описание Позволяет узнать, имеет ли данная реализация IDictionary фиксированный размер Позволяет узнать, можно ли модифицировать коллекцию. Keys Возвращает ссылку на коллекцию (ICollection), содержащую список ключей словаря. Values Возвращает ссылку на коллекцию (ICollection), содержащую список значений словаря.
Интерфейс IDictionary Методы Метод Описание Add Позволяет добавить пару ключ/значение к словарю Clear Contains Очищает содержимое коллекции. Remove Позволяет удалить элемент с заданным ключом. Позволяет определить, содержит ли коллекция элемент с заданным ключом. Get. Enumerator Возвращает ссылку на перечислитель словаря – интерфейс IDictionary. Enumerator.
Виды коллекций, включенные в FCL Тип Назначение Встроенные массивы Обычные массивы, поддерживаемые CLR напрямую. Array. List Является реализацией абстракции списка на базе массива, По сути, динамический массив, позволяющий хранить ссылки на объекты. Hashtable Реализует абстракцию «словарь» (Dictionary, коллекцию пар «ключ-значение» ) на основе алгоритма хэш-таблицы. Sorted. List Реализация абстракции словаря и списка на базе сортированного массива. Stack Реализует абстракцию «стек» Queue Реализует абстракцию «очередь» Bit. Array Позволяет управлять битовыми массивами
Методы класса Array Метод Описание Binary. Search Осуществляет поиск заданного значения в одномерном отсортированном массиве с использованием алгоритма бинарного поиска. Index. Of Возвращает индекс первого вхождения значения в одномерном массиве или диапазоне значений. Поиск производится простым перебором. Last. Index. Of Возвращает индекс последнего вхождения значения в одномерном массиве или диапазоне значений. Reverse Меняет порядок следования элементов массива или диапазона на обратный. Sort Сортирует элементы в одномерном массиве (с помощью алгоритма Quick. Sort).
Свойства класса Array Метод Описание Length Возвращает общее число элементов во всех измерениях массива в виде 32 битного целого. Long. Length Возвращает общее число элементов во всех измерениях массива в виде 64 битного целого. Rank Возвращает количество измерений массива.
Достоинства и недостатки массивов • К достоинствам массивов можно отнести высокую скорость доступа по индексу и типизированность. • Недостатки не поддерживают динамического изменения размера
Реализации
Array. List Очень часто случается так, что размер массива неизвестен заранее. Именно в таких случаях удобно использовать класс Array. List – это динамический одномерный массив. public class Array. List : IList, ICollection, IEnumerable, ICloneable В качестве хранилища данных в Array. List используется скрытый массив ссылок на object (object[]). Этот массив обычно имеет размер больший, нежели число хранящихся в Array. List элементов. Реальное количество элементов, хранящихся в данный момент в Array. List, можно узнать из свойства Count.
Общий объем памяти Array. List оперирует понятием емкость (Capacity). Емкость – это реальный размер внутреннего массива, хранящего данные. Емкость не может быть меньше количества элементов. Если при вставке новых элементов емкости не хватает, происходит ее увеличение. Делается это путем присвоения свойству Capacity значения, вдвое большего, чем текущее значение этого же свойства. Увеличение удвоением с одной стороны, минимизирует количество перезаемов памяти (это довольно медленная операция), а с другой - обеспечивает приемлемый расход памяти.
Оптимизация используемой памяти Если вам кажется, что объем памяти, занятой под внутренний массив, избыточен всегда можно уменьшить объем занятой памяти, вызвав метод Trim. To. Size(). Лучше не злоупотреблять этой возможностью, так как эта операция требует относительно большого времени, а GC все равно не освободит память до следующей сборки мусора. Вместо этого лучше при удобном случае пересоздать весь массив. В большинстве алгоритмов этот прием также позволяет упростить код.
Работа с Array. List array = new Array. List(100); Если при создании Array. List не задать емкость, при первой вставке она будет установлена в значение 16. Если нужно скопировать элементы из другой коллекции, можно воспользоваться конструктором: public Array. List(ICollection c);
Работа с Array. List 2 string[] some. List = new string[] { "Основной элемент 1", "Основной элемент 2", "Основной элемент 3", "Основной элемент 4", "Основной элемент 5", "Основной элемент 6", };
Продолжение // Следующая строка функционально аналогична двум // за ней, но приводит к большему расходу памяти. //Array. List array. List = new Array. List(some. List); Array. List array. List = new Array. List(some. List. Length + 3); array. List. Add. Range(some. List); array. List. Add("Дополнительный элемент 1"); array. List. Add("Дополнительный элемент 2"); array. List. Add("Дополнительный элемент 3"); // array. List. Capacity, array. List. Count ///? ? ? Если заменить выделенные красным строки (раскомментировать первую и закомментировать две, идущие за ней) ///? ? ?
Методы Array. List Если требуется вставить содержимое коллекции в произвольное место, можно воспользоваться методом: public virtual void Insert. Range(int index, ICollection c); Чтобы удалить некоторый диапазон элементов, можно воспользоваться методом: public virtual void Remove. Range(int index, int count); Если требуется переписать некоторый диапазон значений значениями из другой коллекции, лучше всего воспользоваться методом: public virtual void Set. Range(int index, ICollection c); Получить значение элементов диапазона массива : public virtual Array. List Get. Range(int index, int count);
Достоинства Array. List К достоинствам Array. List можно отнести: Возможность динамического изменения размера массива. Высокую скорость доступа по индексу. Высокую скорость добавления в конец и удаления последних элементов. Полиморфность. В массиве можно хранить элементы разных типов. Однако это достоинство очень спорно (см. список недостатков).
Недостатки Array. List • • Необходимость постоянного приведения типов при доступе к элементам. И как следствие, меньшую надежность и небольшое уменьшение производительности. Существенное снижение производительности при хранении value-типов вследствие необходимости boxing-а и unboxing-а при доступе к элементам. Низкую производительность в алгоритмах, производящих большое количество вставок и удалений в середину списка. Это происходит потому, что при вставке в середину необходимо производить смещение диапазона элементов, начинающегося с позиции вставки и заканчивающегося концом массива. Особенно неприятно этот недостаток проявляется при увеличении размера массива. Необходимость перезаема памяти превышении имеющейся емкости.
Stack public class Stack : ICollection, ICloneable Так же, как Array. List, реализует интерфейсы ICollection и ICloneable. Таким образом, Stack можно клонировать, а его содержимое можно скопировать во внешний массив. Основные методы этой коллеции: • public virtual void Push(Object obj); • public virtual Object Pop(); • public virtual Object Peek();
Queue public class Queue : ICollection, ICloneable Интерфейс Queue является почти полной копией класса Stack, за исключением того, что вместо методов Push и Pop он реализует методы: public virtual void Enqueue(Object obj); public virtual Object Dequeue();
Queue Очередь в FCL реализована на базе массива. Но используется этот массив несколько иначе. В Queue используется так называемый кольцевой буфер. Вместо того, чтобы все время помещать данные в конец внутреннего массива, в Queue заведены специальные указатели хвоста и головы очереди. Таким образом, вместо того, чтобы сдвигать данные при каждом помещении/извлечении данных, происходит смещение головы и хвоста. Если количества элементов во внутреннем массиве недостаточно для размещения новых элементов, происходит увеличение емкости. В отличие от стека, задать значение емкости для очереди можно только в момент ее создания.
Bit. Array public sealed class Bit. Array : ICollection, ICloneable В отличие от C++, в C# отсутствуют структуры с битовыми полями. Поэтому средствами языка на уровне битов можно работать только с помощью битовых операций. Чтобы упростить работу с наборами битов, в FCL был добавлен класс Bit. Array.
Конструкторы Позволяют создать битовый массив заданной длины, при этом значение каждого бита будет false (биты будут установлены в 0(default. Value)) public Bit. Array(int length); public Bit. Array(int length, bool default. Value); Чтобы задать значения для каждого бита битового массива можно воспользоваться следующими видми конструкторов: public Bit. Array(bool[] values); public Bit. Array(int[] values); public Bit. Array(byte[] bytes);
Методы Чтобы установить значение всех битов массива уже после его создания, можно воспользоваться методом: public void Set. All(bool value); или public void Set(int index, bool value); Узнать значение некоторого бита: public bool Get(int index); Прочитать и установить значения отдельных битов можно также с помощью индексатора: public bool this[int index] { get; set; }
Операции Над экземплярами Bit. Array можно производить битовые операции. Для этого используются следующие методы: public Bit. Array And(Bit. Array value) ; public Bit. Array Or(Bit. Array value); public Bit. Array Xor(Bit. Array value); public Bit. Array Not(); Все эти операции производят модифицикацию текущего экземпляра Bit. Array, производя битовые операции над данными текущего массива и массива, полученного в качестве параметра.
Недоработка К сожалению, хотя Bit. Array написан на C# (как и практически вся FCL), для Bit. Array не создано перегруженных операторов. А ведь насколько выразительнее был бы код с их применением. Причем класс предусмотрительно помечен как sealed, так что добавить перегрузку бинарных операторов в его наследнике не выйдет.
Hashtable – это реализация абстракции «словарь» (IDictionary, иногда эту абстракцию называют английским словом Map) на базе хэш-таблицы. Отличительными особенностями алгоритма хэштаблицы являются высокая скорость поиска, вставки и удаления элементов, если поиск осуществляется по ключу. Недостаток заключается в том, что данная коллекция не поддерживает упорядоченности, и не сохраняет порядок следования элементов. Hashtable является идеальной коллекцией, если нужен быстрый поиск значения по некоторому ключу или проверки существования ключа
Скоростные характеристики Хэш-таблицы удерживают почетное второе место после массивов с их прямым доступом по индексу. В лучшем случае скоростная характеристика поиска в хэш-таблице – O(1). В худшем – O(n). Hashtable в. NET написана так, что худшего случая можно добиться, если только расчет хэш-значений для ключей реализован из рук вон плохо Если говорить о реальной скорости, то, хотя скоростные характеристики Hashtable и одинаковы с массивом, все же Hashtable будет работать значительно медленнее массивов (особенно типизированных). Дело в том, что для поиска в хэштаблице нужно как минимум вычислить хэш-код и произвести некоторые операции по извлечению бакетов, а ведь еще бывают и коллизии.
Добавление пар Hashtable позволяет ассоциировать одно значение с другим. Первое значение называется ключом, а второе – непосредственно значением. Функции добавления void Add(Object key, Object value); Object this[Object key]
Использование Add: Hashtable ht = Hashtable(); int some. Val = 123; ht. Add("Тестовый ключ", some. Val); -------------Индексатор Hashtable ht = Hashtable(); int some. Val = 123; ht["Тестовый ключ"] = some. Val;
Перебор ключей и значений Порой возникает необходимость получить данные, хранящиеся в хэш-таблице в виде линейного списка. Hashtable позволяет сделать это двумя способами. Во-первых, можно скопировать данные в массив, а во-вторых, можно воспользоваться одним из итераторов. Скопировать содержимое ключей, значений или их пар, а также получить перечислители для их перебора можно с помощью свойств или методов:
Множество элементов доступа Values Возвращает коллекцию (интерфейс ICollection) значений. Используя это свойство можно перебирать значения , содержащиеся в хэш-таблице, во внешний массив. Keys Возвращает коллекцию (интерфейс ICollection) ключей. Используя это свойство, можно перебирать ключи, содержащиеся в хэш-таблице, во внешний массив. Get. Enumerator Позволяет перебрать пары ключ/значение, хранимые в хэш-таблице. Это самый быстрый способ на сегодня, так как в текущей реализации.
Производительность и качество хэшфункции Скорость поиска в хэш-таблице зависит от двух факторов: размера хэштаблицы и качества хэш-функции. Под качеством хэш-функции понимается ее способность выдавать хэш-значения, дающие минимальное количество коллизий.
Sorted. List Как и Hashtable, Sorted. List является реализацией абстракции «словарь» (IDictionary), но, в отличие от хэштаблицы, поддерживает упорядоченность данных. Достигается это за счет хранения ключей и данных в двух отсортированных массивах. Поиск элемента по ключу у этой коллекции происходит за время O(log 2 n).
Sorted. List Вставка состоит из поиска и сдвижки элементов массивов, начиная от вставляемого и до конца массива. Так как сдвижка при больших размерах массива (десятки тысяч элементов) становится все медленнее, стоит стараться избегать использования этой коллекции, если требуются частные вставки-удаления. Совершенно не оправдано использование этой коллекции там, где нужен только поиск по ключу. В таких ситуациях намного лучше выбрать хэш-таблицу.
Data. Set и Data. Table Не входят в список стандартных коллекций, так как предназначены в первую очередь для работы с данными, выбираемыми из БД. Однако с ними можно полноценно работать и без БД. Data. Table позволяет хранить список строк, каждая из которых состоит из набора ячеек и определяет набор ячеек (полей) для всех строк, входящих в Data. Table. Таким образом, Data. Table очень похож на таблицу БД. В Data. Table даже можно создать индекс или отфильтровать данные по некоторому условию. Данные в Data. Table хранятся практически оптимально. Так что память не будет расходоваться понапрасну. Data. Set является коллекцией Data. Table-ов. В рамках Data. Setа можно определять связи между Data. Tableами. Т. е. Data. Set аналогичен маленькой БД, хранящейся в памяти.
Еще раз о производительности Никакие советы и финты не уберегут вас от катастрофического замедления программ, если вы не умеете подбирать нужный тип коллекции. Ни одна самая быстрая реализации коллекции не может скомпенсировать разницу в алгоритмах. Если вам нужен быстрый поиск по ключу, то лучше всего воспользоваться хеш-таблицей. Если кроме этого нужно сохранять упорядоченность или сортированность, то стоит или поместить элементы кроме хэш-таблицы еще и в массив (отсортировав его после добавления всех элементов, если это требуется). Если предполагается часто добавлять и удалять элементы, и при этом требуются упорядоченность и поиск по ключу, то лучше всего воспользоваться Sorted. List-ом. Однако нужно помнить, что скорость поиска по ключу у него ниже, а вставка и удаление – довольно дорогие операции.
Еще раз о производительности 2 Если вам просто требуется сохранять упорядоченность, то лучше всего выбрать массив или Array. List лучше выбирать, если требуются массовые вставки и/или удаления, а простой массив – если нужно хранить относительно статичные данные. Если данные формируются в начале работы, а дальше не изменяются, то лучше всего сформировать список с использованием Array. List, и по окончании скопировать данные в обычный массив. Это повысит типобезопасность и поднимет производительность. Старайтесь избегать вставки элементов в середину Array. List. Лучше добавлять элементы в конец. Если по сценарию вашего приложения данные должны быть упорядочены, лучше всего сначала добавить данные в конец массива, а потом один раз его отсортировать. Если алгоритм допускает хранение данных как в одном Array. List, так и в нескольких, лучше стараться разнести данные по нескольким Array. List, так как удаление и вставка в середину списка при этом будут происходить быстрее
Еще раз о производительности 3 Если необходимо передать коллекцию вовне (возвратить из публичной функции и т. п. ), лучше пользоваться типизированными коллекциями или (на худой конец) интерфейсами IList или ICollection (а так же их generic-вариациями). Это позволит избежать ошибок дизайна, а значит многих граблей, связанных с ним. Если вам доступен. NET Framework 1. 2 и выше, пользуйтесь generic-коллекциями вместо обычных. Это повысит производительность и безопасность кода. Не ленитесь комбинировать коллекции. Помните, что панацей не бывает.
Коллекции, которых нет в. NET Framework • Реализации списков на базе одно- и двунаправленных связанных списков. Они проигрывают массивам практически по всем параметрам, кроме одного. Удаление и добавление элементов в середину списка производится значительно быстрее (если, конечно, позиция вставки/удаления, т. е. ссылка на элемент, хранится отдельно). Комбинация хэш-таблицы и двунаправленного связанного списка может оказаться оптимальным выбором, если требуется быстрый поиск по ключу и последующий перебор последовательности элементов. • Деревья. В список полезных коллекций можно было бы добавить автоматические балансируемые бинарные деревья поиска (такие как АВЛ-деревья и Красно-Черные деревья (КЧД)). Они позволяют упорядочивать данные и не приводят к значительному замедлению вставки и удаления элементов при росте их количества. Есть также структуры типа Б и Б+деревьев. Они интересны, если требуется хранить очень большие объемы данных, особенно если данные уже нельзя уместить в памяти. Хотя тут уже скорее всего стоит задуматься о применении СУБД. • Разновидности очередей, как например, deque (очередь с двусторонним доступом).