Читайте также:
|
|
Общеизвестно, что для того чтобы перебрать все элементы в некоторой коллекции используется метод foreach. Для тех, кто никогда не создавал собственные коллекции позволяющие перебирать элементы этот механизм был неизвестен, поэтому стоит описать процесс создания коллекции, поддерживающей последовательный перебор элементов с помощью синтаксиса foreach.
Для того чтобы коллекция поддерживала foreach необходимо реализовать метод GetEnumerator, возвращающий специальный класс, с помощью которого производится определение порядка вывода элементов.
Итераторы в.NET Framework называются 'перечислителями' (enumerators) и представлены интерфейсом IEnumerator. IEnumerator реализует метод MoveNext(), который переходит к следующему элементу и указывает, достигнут ли конец коллекции; свойство Current служит для получения значения указываемого элемента; дополнительный метод Reset() возвращает перечислитель на его исходную позицию. Перечислитель первоначально указывает на специальное значение перед первым элементом, поэтому вызов MoveNext() необходим для начала итерации.
Перечислители обычно передаются вызовом метода GetEnumerator() объекта, реализующего интерфейс IEnumerable. Классы контейнеров обычно реализуют этот интерфейс. Тем не менее, выражение foreach в языке C# может оперировать любым объектом, поддерживающим подобный метод, даже если он не реализует IEnumerable. Оба интерфейса были расширены в обобщенных версиях.NET 2.0.
// 'явная' версия
IEnumerator<MyType> iter = list.GetEnumerator();
while (iter.MoveNext())
Console.WriteLine(iter.Current);
// 'неявная' версия
foreach (MyType value in list)
Console.WriteLine(value);
C# 2.0 также поддерживает генераторы: метод, объявляемый как возвращаемый IEnumerator (или IEnumerable), но использующий выражение «yield return» (гибкое возвращение) для создания последовательности элементов вместо возвращения сущности объекта, будет превращен компилятором в новый класс, реализующий соответствующий интерфейс.
Шаблон проектирования «Итератор» предназначен для последовательного доступа ко всем элементам коллекции (агрегата), не раскрывая ее внутренней структуры. Это один из классических шаблонов проектирования.
Язык C# поддерживает только однонаправленные итераторы, которые не поддерживают ничего кроме получения текущего элемента, перемещения на следующий элемент и перемещение в начало коллекции (причем последняя возможность не является обязательной).
Для реализации итератора на языке C# нужно выполнить одно из двух условий. Во-первых, вы можете просто реализовать интерфейс IEnumerable или его «обобщенный» вариант – IEnumerable<T> (*), во-вторых, ваша коллекция может просто содержать метод GetEnumerable, который, в свою очередь возвратит сущность, содержащую свойство Current и метод MoveNext.
Давайте начнем с менее распространенного варианта, который основан не на реализации интерфейсов IEnumerable или IEnumerable<T>, а на соответствии кода приведенному выше шаблону (реализация итератора с помощью одного из интерфейсов IEnumerable является аналогичной, просто хочется подчеркнуть, что реализация интерфейса IEnumerable не является обязательной).
Итератор — метод доступа get или оператор, выполняющий настраиваемую итерацию класса массива или коллекции с помощью ключевого слова yield. При использовании оператора return ключевого слова yield элемент в исходной последовательности немедленно возвращается вызывающему объекту до того, как будет получен доступ к следующему элементу. Хотя итератор создается как метод, компилятор переводит его во вложенный класс, который фактически является конечным автоматом. Данный класс отслеживает положения итератора, пока в клиентском коде выполняется цикл foreach.
Итератор вызывается из клиентского кода с помощью оператора foreach. Например, можно создать итератор для класса, возвращающего элементы в обратном порядке или выполняющего операцию над каждым элементом перед тем, как итератор возвратит его. При создании итератора для класса или структуры реализация всего интерфейса IEnumerator не требуется. Когда компилятор обнаруживает итератор, он автоматически создает методы Current, MoveNext и Dispose интерфейса IEnumerator или IEnumerator<T>.
Итератор — это раздел кода, возвращающий упорядоченную последовательность значений одинакового типа.
Итератор может использоваться в качестве основной части метода, оператора или метода доступа get.
В коде итератора для возвращения всех элементов по очереди используется оператор yield return.yield break завершает итерацию.
В классе можно реализовать несколько итераторов. Каждый итератор должен иметь уникальное имя, как и любой член класса, и может вызываться клиентским кодом в операторе foreach: foreach(int x in SampleClass.Iterator2){}.
Тип возвращаемого итератором значения должен быть IEnumerable, IEnumerator, IEnumerable<T> или IEnumerator<T>.
Итераторы используются для обеспечения отложенного выполнения в запросах LINQ.
Ключевое слово yield используется для указания возвращаемого значения или значений. При подходе к оператору yield return текущее положение сохраняется. При следующем вызове итератора выполнение возобновляется с этого места.
Итераторы также удобно использовать с классами коллекций, так как это обеспечивает простой способ итерации сложных структур данных, например двоичных деревьев.
30. Атрибуты в среде.NET и языке C#. Создание своих атрибутов.
Атрибуты предоставляют эффективный метод связывания декларативной информации с кодом C# (типы, методы, свойства и т. д.). Атрибут, связанный с сущностью программы, может быть запрошен во время выполнения с помощью метода, называемого отражением.
Существует две формы атрибутов.
Атрибуты, которые определены в среде CLR.
Пользовательские атрибуты, которые можно создать, чтобы добавить в код дополнительные сведения. Затем эти сведения можно извлечь программным путем.
Атрибуты имеют следующие параметры.
Атрибуты добавляют в программу метаданные. Метаданные представляют собой сведения о типах, определенных в программе. Все сборки.NET содержат заданный набор метаданных, описывающий типы и члены типов, определенных в сборке. Для задания необходимых дополнительных сведений можно добавить атрибуты.
Программа может проверить собственные метаданные или метаданные в других программах с помощью отражения.
Атрибуты, например MarshallAsare, широко используемые в сценариях COM-взаимодействия.
Атрибуты могут быть размещены в большинстве объявлений, хотя определенный атрибут может ограничить типы объявлений, в которых он допустим. Синтаксически атрибут указывается путем размещения его имени, заключенного в квадратные скобки, в начале объявления сущности, к которой он применяется. Например, объявление метода с атрибутом DllImport выглядит следующим образом.
[System.Runtime.InteropServices.DllImport("user32.dll")]
extern static void SampleMethod();
Многие атрибуты имеют параметры, которые могут быть либо позиционными, неименованными, либо именованными. Именованные параметры следует указывать в определенном порядке, их нельзя опустить; именованные параметры являются необязательными и могут быть указаны в любой последовательности. Сначала указываются позиционные параметры. Например, следующие три атрибута являются эквивалентными.
[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]
Первый параметр — имя библиотеки DLL — является позиционным и всегда стоит на первом месте; остальные являются именованными. В этом случае оба именованных параметра по умолчанию имеют значение "false" и могут быть пропущены.
В объявлении можно разместить несколько атрибутов — либо по отдельности, либо в одном наборе скобок.
void MethodA([In][Out] ref double x) { }
void MethodB([Out][In] ref double x) { }
void MethodC([In, Out] ref double x) { }
Можно создать собственные настраиваемые атрибуты, задав класс атрибутов, который явно или косвенно наследует от Attribute, что облегчает и ускоряет определение атрибутов в метаданных. Предположим, что необходимо пометить классы и структуры именем программиста, который создал класс или структуру. Можно задать класс настраиваемых атрибутов Author:
[System.AttributeUsage(System.AttributeTargets.Class |
System.AttributeTargets.Struct)
]
public class Author: System.Attribute
{
private string name;
public double version;
public Author(string name)
{
this.name = name;
version = 1.0;
}
}
В качестве имени класса используется имя атрибута Author. Он является производным от System.Attribute, следовательно, это класс настраиваемых атрибутов. Параметры конструктора являются позиционными параметрами настраиваемого атрибута, тогда name и все открытые для чтения и записи поля или свойства являются именованными параметрами, в этом случае единственным именованным параметром является version. Обратите внимание на то, что атрибут AttributeUsage используется для того, чтобы атрибут Author был действителен только для объявлений class и struct.
Дата добавления: 2015-01-30; просмотров: 77 | Поможем написать вашу работу | Нарушение авторских прав |