Программирование >>  Программирование с использованием ajax 

1 ... 92 93 94 [ 95 ] 96 97 98 ... 396


public Animal this[string animallD] {

get {

return (Animal)Dictionary[animallD];

set {

Dictionary[animallD] = value;

Отличия этих членов описаны ниже.

□ Метод Add(). Принимает два параметра, ключ и значение, для совместного хранения. У словарной коллекции имеется член по имени Dictionary, унаследованный от DictionaryBase, который представляет собой интерфейс IDictionary. У этого интерфейса имеется свой собственный метод Add(), который принимает два параметра-объекта. В данной реализации он принимает строковое значение в качестве ключа и объект Animal в качестве данных, подлежащих сохранению вместе с этим ключом.

□ Метод Remove О . Принимает параметр-ключ, а не ссылку на объект. Элемент с указанным значением ключа удаляется.

□ Индексатор. Использует не индекс, а строковое значение-ключ, которое применяется для получения доступа хранимому элементу через унаследованный член Dictionary. Опять-таки, приведение типов здесь является необходимым.

Еще одно отличие между коллекциями на базе DictionaryBase и коллекциями на базе CollectionBase состоит в том, что немного по-другому работает структура foreach. Класс коллекции, демонстрировавшийся в предыдущем разделе, позволял извлекать из коллекции непосредственно объекты Animal. Применение foreach с производным классом DictionaryBase позволяет получать структуры DictionaryEntry, которые представляют собой еще один тип, определенный в пространстве имен System.Collections. Из-за этого для получения самих объектов Animal требуется использовать такой член этой структуры, как Value, или, для получения ассоциируемых с ними ключей, такой член, как Key. То есть, чтобы получить код, эквивалентный прежнему:

foreach (Animal myAnimal in animalCollection) {

Console.WriteLine( New {0} ob]ect added to custom collection, +

Name = {1} , myAnimal.ToString (), myAnimal.Name);

необходимо сделать следующее:

foreach (DictionaryEntry myEntry in animalCollection) {

Console.WriteLine ( New {0} object added to custom collection, + Name = (1} , myEntry.Value.ToString(), ((Animal)myEntry.Value).Name);

Существует возможность переопределить это поведение так, чтобы к объектам Animal можно было получать доступ непосредственно через foreach. Для этого доступно несколько способов, самым простым из которых является реализация итератора.



Итераторы

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

Однако переопределение этого поведения, т.е. предоставление для него своей собственной реализации, не всегда выглядит просто. Чтобы увидеть это, сначала необходимо более внимательно рассмотреть сами циклы foreach. Перечисленные ниже шаги описывают, что на самом деле происходит в цикле foreach, осуществляющем проход по коллекции с именем collectionObject.

1. Сначала вызывается метод collectionObject.GetEnumerator (), который возвращает ссылку lEnumerator. Этот метод доступен через реализацию интерфейса lEnumerable, хотя таковая является необязательной.

2. Далее вызывается такой метод возвращенного интерфейса lEnumerator, как MoveNext().

3. Если метод MoveNext () возвращает true, используется такое свойство интерфейса lEnumerator, как Current, для получения ссылки на объект, которая далее применяется в цикле foreach.

4. Два последних шага повторяются до тех пор, пока MoveNext () не вернет false, после чего цикл завершается.

Для обеспечения такого поведения в своих классах потребуется переопределить несколько методов, отслеживать индексы, обслуживать свойство Current и т.д. - другими словами, прикладывать слишком много усилий для достижения столь малого результата.

К счастью, существует и более простой альтернативный вариант - итератор. Применение итераторов, по сути, приводит к генерации за кулисами множества кода и его корректной привязке. Более того, синтаксис для использования операторов является гораздо более простым для изучения.

Хорошее определение для итератора звучит так: итератор - это блок кода, который предоставляет все значения, подлежащие использованию в блоке foreach, по очереди. Обычно в роли этого блока кода выступает метод, хотя в роли итератора также может использоваться и блок, отвечающий за доступ к свойству, или какой-нибудь другой блок кода. Чтобы не усложнять дело, здесь будут демонстрироваться только методы.

Каким бы не был этот блок кода, количество возможных кандидатов на роль его возвращаемого типа ограничено. Пожалуй, вопреки ожиданиям, этот возвращаемый тип не может совпадать с типом того объекта, который перечисляется. Например, в классе, представляющем коллекцию объектов Animal, возвращаемым типом блока итератора не может быть Animal. Двумя допустимым кандидатами на роль возвращаемого значения являются типы упоминавшихся раньше интерфейсов lEnumerable или lEnumerator. Применяются они следующим образом.

□ Для осуществления итерации по классу применяется метод GetEnumerator () с возвращаемым типом lEnumerator.

□ Для осуществления итерации по члену класса, например, методу, применяется

lEnumerable.

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

yield return значение;



Приведенной информации уже вполне достаточно для того, чтобы создать очень простой пример, вроде того, что показан ниже:

public static lEnumerable SimpleListO

yield return string 1 ; yield return string 2 ; yield return string 3 ;

public static void Main(string [ ] args) {

foreach (string item in SimpleListO)

Console.WriteLine(item); Console.ReadKey0;

При желании самостоятельно протестировать данный код, не забудьте добавить оператор using для пространства имен System, Collections или, в противном случае, полностью квалифицируйте имя интерфейса System, Collections, lEnumerable. В качестве альтернативного варианта, можете просто воспользоваться загружаемым кодом для этой главы.

Здесь в роли итератора выступает статический метод SimpleList (). Поскольку эту роль выполняет метод, в качестве возвращаемого типа применяется тип lEnumerable. Сам метод SimpleList () использует ключевое слово yield для предоставления использующему его блоку foreach трех значений, каждое из которых выводится на экран. Результат показан на рис. 11.3.


Рис. 11.3. Метод-итератор SimpleList О в действии

Очевидно, что данный итератор не является особо полезным, но зато действительно показывает, как все это работает на практике и насколько просто может выглядеть реализация. После просмотра кода может возникнуть вопрос, откуда он знает, что нужно возвращать элементы типа string? На самом деле он этого не знает; он возвращает объекты типа object. Как уже известно, object является базовым классом для всех типов, благодаря чему из операторов yield можно возвращать все что угодно.

Однако компилятор является достаточно разумным для того, чтобы позволить интерпретировать возвращаемые значения как значения любого желаемого типа в контексте цикла foreach. Здесь код запрашивает значения типа string, поэтому они и предоставляются для работы. В случае изменения одной из строк yield таким образом, чтобы она возвращала, скажем, целое число, будет сгенерировано исключение, связанное с неправильным приведением в цикле foreach.

Обратите внимание на еще один аспект, касающийся итераторов. Существует возможность прерывать возврат информации циклу foreach с использованием следующего оператора:

yield break;



1 ... 92 93 94 [ 95 ] 96 97 98 ... 396

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика