Итераторы
Реализация
Реализовать итеративный доступ к данным можно двумя способами. Первый, достаточно очевидный, посредством реализации интерфейса 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 или сериализуемы).