- •А.А. Волосевич
- •1. Работа с Числами
- •2. Дата и время
- •3. Работа со строками и текстом
- •4. Преобразование информации
- •5. Отношения равенства и порядка
- •Сравнение для выяснения равенства
- •Сравнение для выяснения порядка
- •6. Жизненный цикл объектов
- •Алгоритм «сборки мусора»
- •Финализаторы и интерфейс iDisposable
- •7. Перечислители и итераторы
- •8. Интерфейсы стандартных коллекций
- •9. Массивы и класс system.Array
- •10. Типы для работы с коллекциями-списками
- •11. Типы для работы с коллекциями-множествами
- •12. Типы для работы с коллекциями-словарями
- •13. Типы для создания пользовательских коллекций
- •14. Технология linq to objects
- •1. Оператор условия Where().
- •2. Операторы проекций.
- •3. Операторы упорядочивания.
- •4. Оператор группировки GroupBy().
- •5. Операторы соединения.
- •6. Операторы работы с множествами.
- •7. Операторы агрегирования.
- •8. Операторы генерирования.
- •9. Операторы кванторов и сравнения.
- •10. Операторы разбиения.
- •11. Операторы элемента.
- •12. Операторы преобразования.
- •15. Работа с объектами файЛовой системы
- •16. Ввод и вывод информации
- •Потоки данных и декораторы потоков
- •2. Классы для работы с потоками, связанными с хранилищами.
- •3. Декораторы потоков.
- •4. Адаптеры потоков.
- •Адаптеры потоков
- •17. Основы xml
- •18. Технология linq to xml
- •Создание, сохранение, загрузка xml
- •Запросы, модификация и трансформация xml
- •Пространства имён xml
- •19. ДОполнительные возможности обработки xml
- •20. Сериализация
- •Сериализация времени выполнения
- •Сериализация контрактов данных
- •21. Состав и взаимодействие сборок
- •22. Метаданные и получение информации о типах
- •23. Позднее связывание и кодогенерация
- •24. Динамические типы
- •25. Атрибуты
- •26. Файлы конфигуРации
- •27. Основы мНогопоточноГо программирования
- •28. Синхронизация потоков
- •29. Библиотека параллельных расширений
- •Параллелизм на уровне задач
- •Параллелизм при императивной обработке данных
- •Параллелизм при декларативной обработке данных
- •Обработка исключений и отмена выполнения задач
- •Коллекции, поддерживающие параллелизм
- •30. Асинхронный вызов методов
- •31. Процессы и домены
- •32. Безопасность
- •Разрешения на доступ
- •Изолированные хранилища
- •Криптография
- •33. Диагностика
13. Типы для создания пользовательских коллекций
Типы коллекций, описанные в предыдущих параграфах, применимы в большинстве стандартных ситуаций. Однако иногда требуется создать собственный тип-коллекцию. Например, в случае, когда изменение коллекции должно генерировать событие, или когда необходима дополнительная проверка данных при помещении их в коллекцию. Для облегчения решения этой задачи платформа .NET предлагает несколько классов, размещённых в пространстве имён System.Collections.ObjectModel.
Универсальный класс Collection<T> является настраиваемой оболочкой над классом List<T>1. В дополнение к реализации интерфейсов IList<T> и IList, класс Collection<T> определяет четыре виртуальные метода ClearItems(), InsertItem(), RemoveItem(), SetItem() и свойство для чтения Items, имеющее тип IList<T>. Переопределяя виртуальные методы, можно модифицировать нормальное поведение класса List<T> при изменении набора.
Рассмотрим пример использования Collection<T>. Пусть класс Track представляет отдельную композицию музыкального альбома. Класс Album наследуется от Collection<Track> и описывает альбом. Виртуальные методы класса Collection<Track> переопределяются, чтобы корректно изменять значение свойства Album у объекта Track.
public class Track
{
public string Title { get; set; }
public uint Length { get; set; }
public Album Album { get; internal set; }
}
public class Album : Collection<Track>
{
protected override void InsertItem(int index, Track item)
{
base.InsertItem(index, item);
item.Album = this;
}
protected override void SetItem(int index, Track item)
{
base.SetItem(index, item);
item.Album = this;
}
protected override void RemoveItem(int index)
{
this[index].Album = null;
base.RemoveItem(index);
}
protected override void ClearItems()
{
foreach (Track track in this)
{
track.Album = null;
}
base.ClearItems();
}
}
У класса Collection<T> имеется конструктор, принимающий в качестве параметра объект, реализующий IList<T>. В отличие от других классов коллекций, этот набор не копируется – запоминается ссылка на него. То есть, изменение набора будет означать изменение коллекции Collection<T> (хотя и без вызова виртуальных методов).
Класс ReadOnlyCollection<T> ‑ это наследник Collection<T>, предоставляющий доступ для чтения элементов, но не для модификации коллекции. Конструктор класса принимает в качестве параметра объект, реализующий IList<T>. Класс не содержит открытых методов добавления или удаления элемента, но можно получить доступ к элементу по индексу и изменить его.
var album = new Album
{
new Track {Title = "Speak To Me", Length = 68},
new Track {Title = "Breathe", Length = 168},
new Track {Title = "On The Run", Length = 230}
};
var albumReadOnly = new ReadOnlyCollection<Track>(album);
albumReadOnly[1].Title = string.Empty;
Класс ObservableCollection<T> ‑ это коллекция, позволяющая отслеживать модификации своего набора данных. Этот класс наследуется от Collection<T> и реализует интерфейс INotifyCollectionChanged, который описывает событие, генерируемое при изменении данных:
public interface INotifyCollectionChanged
{
event NotifyCollectionChangedEventHandler CollectionChanged;
}
Аргумент события CollectionChanged позволяет узнать, какое действие выполнено над набором данных (добавление, удаление, замена элемента), а также получить информацию о новых или удалённых элементах коллекции.
// коллекция album – такая же, как в предыдущем примере
var albumObservable = new ObservableCollection<Track>(album);
albumObservable.CollectionChanged +=
delegate(object sender, NotifyCollectionChangedEventArgs e)
{
Console.WriteLine("Action: {0}", e.Action);
foreach (Track item in e.NewItems)
Console.WriteLine("Title: {0}", item.Title);
};
albumObservable.Add(new Track { Title = "Time", Length = 424 });
Абстрактный класс KeyedCollection<TKey, TItem> является наследником Collection<T>. Этот класс добавляет возможность обращения к элементу по ключу (как в словарях). При использовании KeyedCollection<TKey, TItem> требуется переопределить метод GetKeyForItem() для вычисления ключа элемента.
Для демонстрации применения KeyedCollection<TKey, TItem> модифицируем пример с классами Track и Album:
public class Track
{
private string _title;
public string Title
{
get { return _title; }
set
{
if (Album != null && _title != value)
{
Album.ChangeTitle(this, value); // изменение ключа
}
_title = value;
}
}
public uint Length { get; set; }
public AlbumDictionary Album { get; internal set; }
}
public class AlbumDictionary : KeyedCollection<string, Track>
{
protected override string GetKeyForItem(Track item)
{
return item.Title; // ключом будет название композиции
}
internal void ChangeTitle(Track track, string title)
{
ChangeItemKey(track, title); // метод меняет ключ элемента
}
// методы ClearItems(), InsertItem(), RemoveItem(), SetItem()
// реализованы также, как в классе Album
}
// пример использования
var album = new AlbumDictionary
{
new Track {Title = "Speak To Me", Length = 68},
new Track {Title = "Breathe", Length = 168},
new Track {Title = "On The Run", Length = 230}
};
album[0].Length = 0; // обращение по индексу
album["Speak To Me"].Length = 68; // обращение по ключу