- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
carArray[2] = new Car("Zippy, 30, 0);
carArray[3] = new Car("Fjred", 30, 0);}
public IEnumerator GetEnumerator() {
// Возвращает IEnumerator объекта массива.
return carArray.GetEnumerator();
}
}
Теперь, после модификации типа Garage, вы можете использовать этот тип в конструкции foreach без опасений. К тому же, поскольку метод GetEnumerator() определен, как открытый, пользователь объекта тоже может взаимодействовать с типом IEnumerator.
// Manually work with IEnumerator.
IEnumerator I = carLot.GetEnumerator();
i.MoveNext();
Car myCar = (Car)i.Current;
Console.WriteLine("{0} имеет скорость {1} км/ч", myCar.PetName, myCar.CurrSpeed);
Если вы предпочтете скрыть функциональные возможности IEnumerable на объектном уровне, то следует использовать явную реализацию интерфейса.
public IEnumerator IEnumerable.GetEnumerator() {
// Возвращает IEnumerator объекта массива.
return carArray.GetEnumerator();
}
Исходный код. Проект CustomEnumerator размещен в подкаталоге, соответствующем главе 7.
Методы итератора в C#
В .NET 1.x для того, чтобы пользовательские коллекции (такие, как Garage) допускали применение конструкции foreach в операциях, подобных перечислению, реализация интерфейса IEnumerable (и, как правило, интерфейса IEnumerator) была обязательной. В C# 2005 предлагается альтернативный вариант построения типов, позволяющих применение цикла foreach, – с помощью итераторов.
В упрощённой интерпретации итератор является членом, указывающим порядок возвращения внутренних элементов контейнера при их обработке с помощью foreach. И хотя метод итератора все равно должен называться GetEnumerator(), а возвращаемое значение все равно должно иметь тип IEnumerator, при таком подходе ваш пользовательский класс уже не обязан реализовывать все ожидаемые интерфейсы.
public class Garage { // Без реализации IEnumerator!
private Car[] carArray;
…
// Метод итератора.
public IEnumerator GetEnumerator() {
foreach (Car с in carArray) {
yield return c;
}
}
}
Обратите внимание на то, что данная реализация GetEnumerator() осуществляет "проход" по вложенным элементам, используя внутреннюю логику foreach, и возвращает объекты Car вызывающей стороне, используя новую синтаксическую конструкцию yield return. Ключевое слово yield используется для того, чтобы указать значение (или значения), возвращаемые конструкции foreach вызывающей стороны. Когда в программе встречается оператор yield return, сохраняется текущая позиция, и именно с этой позиции выполнение будет продолжено при следующем вызове итератора.
Когда компилятор C# обнаруживает метод итератора, в рамках области видимости соответствующего типа (в данном случае это Garage) динамически генерируется вложенный класс. Этот автоматически сгенерированный класс реализует интерфейсы IEnumerable и IEnumerator и указывает необходимые параметры членов GetEnumerator(), MoveNext(), Reset() и Current. Если теперь загрузить данное приложение в ildasm.exe, то будет видно, что внутренняя реализация GetEnumerator() в объекте Garage использует сгенерированный компилятором тип (который в данном примере получает имя ‹GetEnumerator›d__0).
.method public hidebysig instance class [mscorlib] System.Collections.IEnumerator GetEnumerator() cil managed {
…
newobj instance void CustomEnumeratorWithYield.Garage/ '‹GetEnumerator›d__0'::.ctor(int32)
…
} // end of method Garage::GetEnumerator
Явно, что от предложенного здесь определения метода итератора мы не получим большой пользы, поскольку наш тип Garage изначально реализовывал GetEnumerator(), ссылаясь на внутренний тип System.Array. Но синтаксис итератора C# может сэкономить немало времени при построении более "экзотических" пользовательских контейнеров (например, бинарных деревьев), где приходится вручную реализовать интерфейсы IEnumerator и IEnumerable. В любом случае программный код вызывающей стороны при взаимодействии с методом итератора с использованием foreach оказывается одинаковым.
static void Main(string[] args) {
Console.WriteLine("***** Забавы с методами итератора *****n");
Garage carLot = new Garage();
foreach (Car с in carLot) {
Console.WriteLine("{0} имеет скорость {1} км/ч", с.PetName, с.CurrrSpeed);
}
Console.ReadLine();
}
Исходный код. Проект CustomEnumeratorWifhYield размещен в подкаталоге, соответствующем главе 7.
Создание клонируемых объектов (ICloneable)
Вы, должно быть, помните из главы 3, что System.Object определяет член с именем MemberwiseClone(). Указанный метод используется для получения поверхностной копии объекта. Пользователи объекта не могут вызвать этот метод непосредственно (поскольку он является защищенным), но сам объект может вызвать этот метод в процессе клонирования. Для примера предположим, что у нас есть класс с именем Point (точка).
// Класс Point.
public class Point {
// Открыты для простоты.
public int x, у;
public Point(int x, int y) { this.x = x; this.у = у; }
public Point(){}
// Переопределение Object.ToString().
public override string ToString() { return string.Format("X = {0}; Y = {1}", x, у); }
}
С учетом того, что вы уже знаете о ссылочных типах и типах, характеризуемых значениями (см. главу 3), вы должны понимать, что в результате присваивания одной ссылочной переменной другой получаются две ссылки, указывающие на один и тот же объект в памяти. Поэтому следующее присваивание дает две ссылки на один и тот же объект Point в динамической памяти, и модификации любой из этих ссылок будут влиять на этот объект.
static void Main(string[] args) {
// Две ссылки на один и тот же объект!
Point p1 = new Point(50, 50);
Point p2 = p1;
р2.х = 0;
Console.WriteLine(p1);
Console.WriteLine(p2);
}
Чтобы обеспечить пользовательскому типу возможность возвращать копию этого типа вызывающей стороне, можно реализовать стандартный интерфейс ICloneable. Этот интерфейс определяет единственный метод с именем Clone().
public interface ICloneable {
object Clone();
}
Очевидно, что реализация метода Clone() будет зависеть от объекта. Но базовые функциональные возможности оказываются одинаковыми: это копирование значений членов-переменных в новый экземпляр объекта и возвращение этого экземпляра пользователю. В качестве иллюстрации рассмотрите следующую модификацию класса Point.
// Теперь Point поддерживает клонирование.
public class Point: ICloneable {
public int x, y;
public Point(){}
public Point (int x, int y) { this.x = x; this.у = у; }
// Возвращение копии данного объекта.
public object Clone() { return new Point(this.x, this.y); }
public override string ToString() { return String.Format("X = {0}; Y = {1}", x, у); }
}
С помощью указанного подхода можно создавать точные и независимые копии типа Point, как показано в следующем фрагменте программного кода.
static void Main (string[] args) {
// Обратите внимание, Clone() возвращает объект общего типа.
// Для получении производного типа используйте явное преобразование.
Point р3 = new Point(100, 100);
Point р4 = (Point)р3.Clone();
// Изменение p4.х (это не изменит р3.х).
р4.х = 0;
// Вывод объектов.
Console.WriteLine(р3);
Console.WriteLine(p4);
}
Текущая реализация Point решает все поставленные задачи, но вы можете немного усовершенствовать процесс. Введу того, что тип Point не содержит переменных ссылочного типа, можно упростить реализацию метода Clone(), как показано ниже.
public object Clone() {
// Скопировать все поля Point "почленно".
return this.MemberwiseClone();
}
При наличии в Point членов-переменных ссылочного типа метод MemberwiseClone() скопирует ссылки на соответствующие объекты (т.е. выполнит поверхностное копирование). Чтобы обеспечить поддержку полного копирования объектов, вы должны в процессе клонирования создать новый экземпляр для каждой переменной ссылочного типа. Соответствующий пример мы сейчас рассмотрим.
Пример клонирования
Предположим, что класс Point содержит член ссылочного типа с именем PointDescription, обеспечивающий поддержку "понятного" имени объекта Point и его идентификационного номера в виде System.Guid (еcли у вас нет опыта применения COM, знайте, что GUID – глобально уникальный идентификатор – это статистически уникальное 128-разрядное значение). Вот соответствующая реализация.
// Этот класс описывает точку.