Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Базовые технологии платформы .NET.docx
Скачиваний:
13
Добавлен:
03.11.2018
Размер:
614.46 Кб
Скачать

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; // обращение по ключу