Библиотеки базовых классов предоставляют удобный обобщенный класс по имени Lazy<>, который определен в пространстве имен System внутри сборки mscorlib.dll. Он позволяет определять данные, которые не будут создаваться до тех пор, пока действительно не начнут применяться в коде. Поскольку класс является обобщенным, при первом его использовании вы должны явно указать тип создаваемого элемента, которым может быть любой тип из библиотек базовых классов .NET Core или специальный тип, построенный вами самостоятельно. Чтобы включить отложенную инициализацию переменной-члена AllTracks, просто приведите код MediaPlayer к следующему виду:
// Объект MediaPlayer имеет объект Lazy
class MediaPlayer
{
...
private Lazy
public AllTracks GetAllTracks()
{
// Возвратить все композиции.
return _allSongs.Value;
}
}
Помимо того факта, что переменная-член AllTracks теперь имеет тип Lazy<>, важно обратить внимание на изменение также и реализации показанного выше метода GetAllTracks(). В частности, для получения актуальных сохраненных данных (в этом случае объекта AllTracks, поддерживающего 10 000 объектов Song) должно применяться доступное только для чтения свойство Value класса Lazy<>.
Взгляните, как благодаря такому простому изменению приведенный далее модифицированный код будет косвенно размещать объекты Song в памяти, только если метод GetAllTracks() действительно вызывается:
Console.WriteLine("***** Fun with Lazy Instantiation *****\n");
// Память под объект AllTracks здесь не выделяется!
MediaPlayer myPlayer = new MediaPlayer();
myPlayer.Play();
// Размещение объекта AllTracks происходит
// только в случае вызова метода GetAllTracks().
MediaPlayer yourPlayer = new MediaPlayer();
AllTracks yourMusic = yourPlayer.GetAllTracks();
Console.ReadLine();
На заметку! Ленивое создание объектов полезно не только для уменьшения количества выделений памяти под ненужные объекты. Этот прием можно также использовать в ситуации, когда для создания члена применяется затратный в плане ресурсов код, такой как вызов удаленного метода, взаимодействие с реляционной базой данных и т.п.
Настройка процесса создания данных Lazy<>
При объявлении переменной Lazy() действительный внутренний тип данных создается с использованием стандартного конструктора:
// При использовании переменной Lazy() вызывается
// стандартный конструктор класса AllTracks.
private Lazy
В некоторых случаях приведенный код может оказаться приемлемым, но что если класс AllTracks имеет дополнительные конструкторы и нужно обеспечить вызов подходящего конструктора? Более того, что если при создании переменной Lazy() должна выполняться какая-то специальная работа (кроме простого создания объекта AllTracks)? К счастью, класс Lazy() позволяет указывать в качестве необязательного параметра обобщенный делегат, который задает метод для вызова во время создания находящегося внутри типа.
Таким обобщенным делегатом является тип System.Func<>, который может указывать на метод, возвращающий тот же самый тип данных, что и создаваемый связанной переменной Lazy<>, и способный принимать вплоть до 16 аргументов (типизированных с применением обобщенных параметров типа). В большинстве случаев никаких параметров для передачи методу, на который указывает Func<>, задавать не придется. Вдобавок, чтобы значительно упростить работу с типом Func<>, рекомендуется использовать лямбда-выражения (отношения между делегатами и лямбда-выражениями подробно освещаются в главе 12).
Ниже показана окончательная версия класса MediaPlayer, в которой добавлен небольшой специальный код, выполняемый при создании внутреннего объекта AllTracks. Не забывайте, что перед завершением метод должен возвратить новый экземпляр типа, помещенного в Lazy<>, причем применять можно любой конструктор по своему выбору (здесь по-прежнему вызывается стандартный конструктор AllTracks).
class MediaPlayer
{
...
// Использовать лямбда-выражение для добавления дополнительного