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.
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);