C# 4.0 полное руководство - 2011
Шрифт:
Ниже приведена обобщенная версия предыдущей программы учета товарных запасов, в которой теперь используется интерфейс I Comparer <Т>. Она дает такой же результат, как и необобщенная версия этой же программы.
// Использовать обобщенный вариант интерфейса IComparer<T>. using System;
using System.Collections.Generic;
// Создать объект типа IComparer<T> для объектов класса Inventory, class CompInv<T> : IComparer<T> where T : Inventory {
// Реализовать интерфейс IComparer<T>. public int Compare(T x, T y) {
return string.Compare(x.name, y.name, StringComparison.Ordinal) ;
}
}
class Inventory { public string name; double cost; int onhand;
public Inventory(string n, double c, int h) { name = n; cost = c; onhand = h;
}
public override string ToString { return
String.Format("{0,-10}
}
}
class GenericIComparerDemo { static void Main {
CompInv<Inventory> comp = new CompInv<Inventory>;
List<Inventory> inv = new List<Inventory>;
// Добавить элементы в список. inv.Add(new Inventory("Кусачки", 5.95, 3));
inv.Add(new Inventory("Отвертки", 8.29, 2)); inv.Add(new Inventory("Молотки", 3.50, 4)); inv.Add(new Inventory("Дрели", 19.88, 8));
Console.WriteLine("Перечень товарных запасов до сортировки:"); foreach(Inventory i in inv) {
Console.WriteLine (" " + i);
}
Console.WriteLine ;
// Отсортировать список, используя интерфейс IComparer. inv.Sort(comp);
Console.WriteLine("Перечень товарных запасов после сортировки:"); foreach(Inventory i in inv) {
Console.WriteLine (" " + i);
}
}
}
Применение класса StringComparer
В простых примерах из этой главы указывать явно способ сравнения символьных строк совсем не обязательно. Но это может потребоваться в тех случаях, когда строки сохраняются в отсортированной коллекции или когда строки ищутся либо сортируются в коллекции. Так, если строки должны быть отсортированы с учетом настроек одной культурной среды, а затем их приходится искать с учетом настроек другой культурной среды, то во избежание ошибок, вероятнее всего, потребуется указать способ сравнения символьных строк. Аналогичная ситуация возникает и при хешировании коллекции. Для подобных (и других) случаев в конструкторах классов некоторых коллекций предусмотрена поддержка параметра типа IComparer. С целью явно указать способ сравнения символьных строк этому параметру передается в качестве аргумента экземпляр объекта класса StringComparer.
Класс StringComparer был подробно описан в главе 21 при рассмотрении вопросов сортировки и поиска в массивах. В этом классе реализуются интерфейсы IComparer, IComparer<String>, IEqualityComparer, а также IEqualityComparer<String>. Следовательно, экземпляр объекта типа StringComparer может быть передан параметру типа IComparer в качестве аргумента. В классе StringComparer определяется несколько доступных только для чтения свойств, возвращающих экземпляр объекта типа StringComparer, который поддерживает различные способы сравнения символьных строк. Как пояснялось в главе 21, к числу этих свойств относятся следующие: CurrentCulture, CurrentCulturelgnoreCase, InvariantCulture, InvariantCulturelgnoreCase, Ordinal, а
В качестве примера ниже показано, как коллекция типа SortedList<TKey, TValue> конструируется для хранения символьных строк, ключи которых сравниваются порядковым способом.
SortedList<string, int> users =
new SortedList<string, int>(StringComparer.Ordinal);
Доступ к коллекции с помощью перечислителя
К элементам коллекции нередко приходится обращаться циклически, например, для отображения каждого элемента коллекции. С этой целью можно, с одной стороны, организовать цикл foreach, как было показано в приведенных выше примерах, а с другой — воспользоваться перечислителем. Перечислитель — это объект, который реализует необобщенный интерфейс IEnumerator или обобщенный интерфейс IEnumerator<T>.
В интерфейсе IEnumerator определяется одно свойство, Current, необобщенная форма которого приведена ниже.
object Current { get; }
А в интерфейсе IEnumerator<T> объявляется следующая обобщенная форма свойства Current.
Т Current { get; }
В обеих формах свойства Current получается текущий перечисляемый элемент коллекции. Но поскольку свойство Current доступно только для чтения, то перечислитель может служить только для извлечения, но не видоизменения объектов в коллекции.
В интерфейсе IEnumerator определяются два метода. Первым из них является метод MoveNext , объявляемый следующим образом.
bool MoveNext
При каждом вызове метода MoveNext текущее положение перечислителя смещается к следующему элементу коллекции. Этот метод возвращает логическое значение true, если следующий элемент коллекции доступен, и логическое значение false, если достигнут конец коллекции. Перед первым вызовом метода MoveNext значение свойства Current оказывается неопределенным. (В принципе до первого вызова метода MoveNext перечислитель обращается к несуществующему элементу, который должен находиться перед первым элементом коллекции. Именно поэтому приходится вызывать метод MoveNext , чтобы перейти к первому элементу коллекции.)
Для установки перечислителя в исходное положение, соответствующее началу коллекции, вызывается приведенный ниже метод Reset .
void Reset
После вызова метода Reset перечисление вновь начинается с самого начала коллекции. Поэтому, прежде чем получить первый элемент коллекции, следует вызвать метод MoveNext.
В интерфейсе IEnumerator<T> методы MoveNext и Reset действуют по тому же самому принципу.