Итераторы
Реализация
Реализовать итеративный доступ к данным можно двумя способами. Первый, достаточно очевидный, посредством реализации интерфейса IEnumerable. Все стандартные классы коллекций FCL реализуют данный интерфейс.
В том случае, когда данные не хранятся, а вычисляются, или просто нужно перечислить фиксированный набор значений, скажем, поля объекта, можно воспользоваться операторами yield return и yield break.
Рассмотрим простой пример, перечисление полей объекта.
public class Editors { private string _name1; private string _name2; private string _name3; public IEnumerable< string > Names { get { yield return _name1; yield return _name2; yield return _name3; } } } |
При использовании любой из этих констркуций, компилятором генерируется анонимный класс, помеченный атрибутом [CompilerGenerated] и реализующий интерфейс IEnumerable. Вся логика перечисления переносится в данный класс, и заменяется кодом возвращающим экземпляр данного класса.
public IEnumerable< string > get_Names() { <get_Names>d__0 d__ = new <get_Names>d__0(-2); d__.<>4__this = this ; return d__; } |
[CompilerGenerated] private sealed class <get_Names>d__0 : IEnumerable< string >, IEnumerable, \ IEnumerator< string >, IEnumerator, IDisposable { ... }; |
Единственное предназначение конструкции yield break, на мой взгляд, создание пустых коллеций, поскольку при налиичии хотя бы одного yield return, компилятором генерируется корректный класс-перечисление. Другими словами, если в коде есть хотя бы один оператор yield return, yield break для выхода можно не использовать. В приведеном ниже примере два класса, один возвращает итератор чётных чисел, второй — пустую коллекцию.
namespace Lib { public interface IDataFilter { IEnumerable< int > Filter(IEnumerable< int > source); } public class EvenNumberFilter : IDataFilter { public EvenNumberFilter() { } public IEnumerable< int > Filter(IEnumerable< int > source) { if (source != null ) { foreach ( int i in source) { if ((i & 1) != 1) yield return i; } } /* второй вариант, с использованием yiled break; if (source == null) yield break; foreach(int i in source) { if ((i & 1) != 1) yield return i; } */ } } public class NullFilter : IDataFilter { public IEnumerable< int > Filter(IEnumerable< int > source) { yield break ; } } } |
Remoting
Анонимные классы, сгенерированные компилятором не наследуются от MarshalByRefObject и не сериализуемы. Передача такого итератора в другой AppDomain приведет к генерации исключения. Для того, что бы объект-итератор, созданный в одном AppDomain (сервере), можно было передать для использования в другом AppDomain (клиенту), его нужно либо реализовать явно, в виде класса наследника MarshalByRefObject либо указать атрибут [Serializable].
Классы стандартных коллекций, входящих в FCL, помечены атрибутом [Serializable], поэтому их использование при remoting-взаимодействии безопасно (в том случае, когда элементы коллекций наследники MarshalByRefObject или сериализуемы).