Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Универсальные коллекции языка С#. Учебное пособие
.pdf
T2, …, TN) задается в заголовке объявления интерфейса между сим-
волами <>.
Общая форма объявления обобщенного интерфейса следующая:
interfaceInterfaceName<T1,T2,...,TN>
{
//...
}
здесь:
InterfaceName – имя интерфейса;
T1, T2, …, TN – имена типов, которые выступают в качестве па-
раметров интерфейса.
Класс, реализующий интерфейс InterfaceName<T1, T2, …, TN>,
должен объявляться следующим образом:
classClassName<T1,T2,...,TN>:InterfaceName<T1,T2,...,TN>
{
//...
}
здесь ClassName – имя класса, реализующего интерфейс InterfaceName.
В простейшем случае интерфейс получает некоторый тип T. В этом
случае общая форма объявления интерфейса и его реализации в классе
имеет вид
interfaceInterfaceName<T>
{
//...
}
classClassName<T>:InterfaceName<T>
{
//...
}
В следующей таблице описаны важные для коллекций интерфейсы,
которые определяют их свойства и особенности поведения.
11

Интерфейсы, используемые в коллекциях C#
р
r
(
())
р
Интерфейс Описание
IEnumerable<T>
ICollection<T>
IList<T>
ISet<T>
IDictionary<TKey, TValue>
Интерфейс IEnumerable необходим,
когда с коллекцией используется
оператор foreach. Этот интерфейс
определяет метод GetEnumerator(),
возвращающий перечислитель, кото-
ый реализует IEnumerato
ICollection<T> – этот интерфейс реализован классами обобщенных коллекций. С его помощью можно получить количество элементов в коллекции (свойство Count) и скопировать коллекцию в массив (метод
СоруТо()). Можно также добавлять
и удалять элементы из коллекции
Add(), Remove(), Clear
Интерфейс IList<T> предназначен
для создания списков, элементы
которых доступны по своим позициям. Этот интерфейс определяет
индексатор, а также способы вставки и удаления элементов в определенные позиции (методы Insert()
и Remove()). IList<T> унаследован
от ICollection<T>
Интерфейс ISet<T> появился в версии .NET 4. Этот интерфейс реализуется множествами. Он позволяет
комбинировать различные множества в объединения, а также проверять, не пересекаются ли два множества. ISet<T> унаследован от
ICollection<T>
Интерфейс IDictionary<TKey, TValue>
реализуется обобщенными классами
коллекций, элементы кото
ых состоят
12

Интерфейс Описание
p
()
р
ILookup<TKey, TValue>
IComparer<T>
IEqualityComparer<T>
IProducerConsumerCollection<T>
Окончание таблицы
из ключа и значения. С помощью
этого интерфейса можно получать
доступ ко всем ключам и значениям,
извлекать элементы по индексатору
типа ключа, а также добавлять и удалять элементы
Подобно IDictionary<TKey, TValue>
поддерживает ключи и значения.
Однако в этом случае коллекция может содержать множественные значения для одного ключа
Интерфейс IComparer<T> реализует
компаратор и используется для сортировки элементов внутри коллекции
с помощью метода Com
are
Интерфейс IEqualityComparer<T>
реализует компаратор, который может быть применен к ключам словаря. Через этот интерфейс объекты
могут быть проверены на эквивалентность друг другу. В .NET 4 этот
интерфейс также реализован массивами и ко
тежами
Интерфейс IProducerConsumerCollection<T> был добавлен в версию .NET 4
для поддержки новых, безопасных
в отношении потоков классов коллекций
КОНТРОЛЬНЫЕ ВОПРОСЫ
1. Что такое интерфейсы (interface) и для чего они используются?
2. Что такое униврсальные интерфейсы? Особенности их описания.
3. Что можно определить в интерфейсы?
4. Можно ли создавать экземпляры интерфейса?
13

ИТЕРАТОРЫ
С помощью итераторов можно получить последовательный доступ
ко всем элементам коллекции, таким как список или массив.
Итератор – включает оператор цикла foreach и метод итератора, который должен возвращать значение одного из следующих типов:
IEnumerable, IEnumerable<T>, IEnumerator или IEnumerator<T>. Методом итератора может быть метод доступа get.
Метод итератора или метод доступа get выполняет настраиваемую
итерацию по коллекции.
return для поочередного возврата каждого элемента. При достижении
инструкции yield return текущее расположение в коде запоминается.
При следующем вызове функции итератора выполнение возобновляется с этого места.
Метод итератора вызывается из клиентского кода с помощью оператора foreach или с помощью запроса LINQ.
В приведенном ниже примере первая итерация цикла foreach приводит к вызову метода итератора SomeNumbers, пока не будет достигнут первый оператор yield return. Эта итерация возвращает значение 3.
Текущее расположение в методе итератора сохраняется. В следующей
итерации цикла выполнение метода итератора возобновляется с того
же места и снова приостанавливается при достижении оператора yield
return. Эта итерация возвращает значение 5. Текущее расположение
в методе итератора снова
как будет возвращено значение 8 и достигнут конец метода итератора.
Для завершения итерации можно использовать оператор yield break.
Метод итератора использует оператор yield
сохраняется. Цикл завершается после того
staticvoidMain()
{
foreach(intnumberinSomeNumbers())
{
Console.Write(number.ToString()+"");
}
//Output:358
Console.ReadKey();
}
publicstaticSystem.Collections.IEnumerableSomeNumbers()
14

{
yieldreturn3;
yieldreturn5;
yieldreturn8;
}
Все примеры в этом разделе, кроме примера простого итератора,
включают директивы using для пространств имен System.Collections
и System.Collections.Generic.
В следующем примере простого итератора метод итератора Even‐
Sequenceимеет один оператор yield return, который находится внутри
цикла for. В методе Main каждая итерация оператора foreach создает
вызов метода итератора EvenSequence, который выполняет следующий
оператор yield return. Таким образом, цикл foreach через метод
итератора EvenSequence получит все целые четные значения, которые принимает переменная number.
staticvoidMain()
{
foreach(intnumberinEvenSequence(5,18))
{
Console.Write(number.ToString()+"");
}
//Рузультат:681012141618
Console.ReadKey();
}
publicstaticSystem.Collections.Generic.IEnumerable<int>
EvenSequence(intfirstNumber,intlastNumber)
{
//Выдаетчетныечислаиздиапазона.
for(intnumber=firstNumber;number<=lastNumber;number++)
{
if(number%2==0)
{
yieldreturnnumber;
}
}
}
15

СОЗДАНИЕ ИТЕРАТОРА
ДЛЯ ПОЛЬЗОВАТЕЛЬСКОГО КЛАССА КОЛЛЕКЦИИ
В следующем примере класс DaysOfTheWeek (дни недели) реализует интерфейс IEnumerable, которому требуется метод GetEnumerator.
Этот метод и является методом итератора, обеспечивающим доступ ко
всем элементам массива days.
Компилятор неявно вызывает метод GetEnumerator при выполнении оператора цикла foreach, который возвращает значение типа
IEnumerator, с его помощью цикл foreach получает
доступ к очеред-
ному элементу массива days.
Метод GetEnumerator возвращает каждую строку поочередно с помощью оператора yield return.
staticvoidMain()
{
DaysOfTheWeekdays=newDaysOfTheWeek();
foreach(stringdayindays)
{
Console.Write(day+"");
}
//Результат:ВсПнВтСрЧтПтСб
Console.ReadKey();
}
publicclassDaysOfTheWeek:IEnumerable
{
privatestring[]days={"Вс","Пн","Вт","Ср",
publicIEnumeratorGetEnumerator()
{
for(intindex=0;index<days.Length;index++)
{
//Возвращаеткаждыйденьнедели.
yieldreturndays[index];
}
}
}
Результатработы:
ВсПнВтСрЧтПтСб
"Чт","Пт","Сб"};
16

ИСПОЛЬЗОВАНИЕ ИТЕРАТОРОВ
С УНИВЕРСАЛЬНЫМ СПИСКОМ
Для работы с универсальными коллекциями требуются универсальные итераторы.
В следующем примере универсальный класс Stack<T> реализует
универсальный интерфейс IEnumerable<T>. Метод Push заносит в элемент массива, находящийся на вершине стека, значение типа T. Универсальный метод GetEnumerator возвращает очередной элемент массива values[index]с помощью оператора yield return.
Помимо универсального метода GetEnumerator должен
быть реализован и неуниверсальный метод GetEnumerator. Это связано с тем, что
IEnumerable<T> наследуется от IEnumerable. Неуниверсальная реализация подчиняется универсальной реализации.
В этом примере используются именованные итераторы для поддержки различных способов итерации по одной и той же коллекции
данных. Эти именованные итераторы являются свойствами
TopToBottom и BottomToTop и методом TopN.
Свойство BottomToTop использует итератор в
staticvoidMain()
{
Stack<int>theStack=newStack<int>();
//Добавитьэлементвстек.
for(intnumber=0;number<=9;number++)
{
theStack.Push(number);
}
//Выбратьэлементизстека.
//foreachприменим,потомучтоtheStackреализует
//IEnumerable<int>.
foreach(intnumberintheStack)
{
Console.Write("{0}",number);
}
Console.WriteLine();
//Output:9876543210
//foreachприменим,потомучто
методе доступа get.
17

//theStack.TopToBottomвозвращаетIEnumerable(целого).
foreach(intnumberintheStack.TopToBottom)
{
Console.Write("{0}",number);
}
Console.WriteLine();
//Результат:9876543210
foreach(intnumberintheStack.BottomToTop)
{
Console.Write("{0}",number);
}
Console.WriteLine();
//Результат:01234
foreach(intnumberintheStack.TopN(6))
{
Console.Write("{0}",number);
}
Console.WriteLine();
//Результат:9876543
Console.ReadKey();
}
publicclassStack<T>:IEnumerable<T>
{
privateT[]values=newT[100];
privateinttop=
publicvoidPush(Tt)
{
values[top]=t;
top++;
}
publicTPop()
{
top‐‐;
returnvalues[top];
}
//ЭтотметодреализуетGetEnumeratorметод.Онпозволяет
//операторуforeach использоватьобъектыкласса.
publicIEnumerator<T>GetEnumerator()
56789
0;
18

{
for(intindex=top‐1;index>=0;index‐‐)
{
yieldreturnvalues[index];
}
}
IEnumeratorIEnumerable.GetEnumerator()
{
returnGetEnumerator();
}
publicIEnumerable<T>TopToBottom
{
get{returnthis;}
}
publicIEnumerable<T>BottomToTop
{
get
{
for(intindex=0;index<=top‐1;index++)
{
yieldreturnvalues[index];
}
}
}
publicIEnumerable<T>TopN(intitemsFromTop)
{
//Возвращаетзаданноечислосвершины.
intstartIndex=itemsFromTop>=top?0:top‐itemsFromTop;
for(intindex=top‐1;index>=startIndex;index‐‐)
{
yieldreturnvalues[index];
}
}
}
Итератор может быть методом или методом доступа get. Итератор
не может использоваться в событии, конструкторе экземпляра, статическом конструкторе или статическом методе завершения.
19

Должно существовать неявное преобразование типа выражения
в операторе yield return в аргумент типа для IEnumerable<T>, возвращаемого итератором. В C# метод итератора не может иметь параметры
in, ref или out. В C# yield не является зарезервированным словом
и имеет специальное значение, только если используется перед ключевым словом return или break.
Хотя итератор создается как метод, компилятор переводит его во
вложенный
Этот класс отслеживает положение итератора, пока в клиентском коде
выполняется цикл foreach.
1. Что такое итератор?
2. В чем особенности метода итератора?
3. Значение какого типа должен возвращать метод итератора?
4. Что такое отношение универсальный итератор?
5. Для чего используется слово yield?
класс, который фактически является конечным автоматом.
КОНТРОЛЬНЫЕ ВОПРОСЫ
ДЕЛЕГАТЫ И ЛЯМБДА-ВЫРАЖЕНИЯ
В методах и методах расширения универсальных коллекций в качестве параметров широко используются параметры типов универсальных делегатов классов Action<>, Func<>, Predicate<>. Через эти
параметры с помощью делегатов мы можем передать в метод требуемую нам функцию для настройки работы метода на особенности решаемой нами задачи.
Делегаты и лямбда-выражения тесно связаны между
где в качестве параметра метода коллекции используется делегат,
в метод при его вызове может быть передано лямбда-выражение.
собой. Везде,
ДЕЛЕГАТЫ
Делегат – это тип, экземпляры которого представляют ссылки на
методы с определенным списком параметров и типом возвращаемого
значения. При создании экземпляра делегата этот экземпляр можно
20
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
