Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

MyResourceWrapper rw2 = new MyResourceWrapper;

В коде явно вызывается метод

Dispose
на объекте rw, поэтому вызов деструктора подавляется. Тем не менее, мы "забыли" вызвать метод
Dispose
на объекте
rw2
; переживать не стоит — финализатор все равно выполнится при обработке объекта сборщиком мусора.

На этом исследование особенностей управления объектами со стороны исполняющей среды через сборку мусора завершено. Хотя дополнительные (довольно экзотические) детали, касающиеся процесса сборки мусора (такие как слабые ссылки и восстановление объектов), здесь не рассматривались, полученных сведений должно

быть вполне достаточно, чтобы продолжить изучение самостоятельно. В завершение главы мы взглянем на программное средство под названием ленивое (отложенное) создание объектов.

Ленивое создание объектов

При создании классов иногда приходится учитывать, что отдельная переменная-член на самом деле может никогда не понадобиться из-за того, что пользователь объекта не будет обращаться к методу (или свойству), в котором она используется. Действительно, подобное происходит нередко. Однако проблема может возникнуть, если создание такой переменной-члена сопряжено с выделением большого объема памяти.

В качестве примера предположим, что строится класс, который инкапсулирует операции цифрового музыкального проигрывателя. В дополнение к ожидаемым методам вроде

Play
,
Pause
и
Stop
вы также хотите обеспечить возможность возвращения коллекции объектов
Song
(посредством класса по имени
AllTracks
), которая представляет все имеющиеся на устройстве цифровые музыкальные файлы.

Создайте новый проект консольного приложения по имени

LazyObjectInstantiation
и определите в нем следующие классы:

// Song.cs

namespace LazyObjectInstantiation

{

// Представляет одиночную композицию.

class Song

{

public string Artist { get; set; }

public string TrackName { get; set; }

public double TrackLength { get; set; }

}

}

// AllTracks.cs

using System;

namespace LazyObjectInstantiation

{

// Представляет все композиции в проигрывателе.

class AllTracks

{

// Наш проигрыватель может содержать

// максимум 10 000 композиций.

private Song[] _allSongs = new Song[10000];

public AllTracks

{

// Предположим, что здесь производится

// заполнение массива объектов Song.

Console.WriteLine("Filling up the songs!");

}

}

}

// MediaPlayer.cs

using System;

namespace LazyObjectInstantiation

{

//
Объект MediaPlayer имеет объекты AllTracks.

class MediaPlayer

{

// Предположим, что эти методы делают что-то полезное.

public void Play { /* Воспроизведение композиции */ }

public void Pause { /* Пауза в воспроизведении */ }

public void Stop { /* Останов воспроизведения */ }

private AllTracks _allSongs = new AllTracks;

public AllTracks GetAllTracks

{

// Возвратить все композиции.

return _allSongs;

}

}

}

В текущей реализации

MediaPlayer
предполагается, что пользователь объекта пожелает получать список объектов с помощью метода
GetAllTracks
. Хорошо, а что если пользователю объекта такой список не нужен? В этой реализации память под переменную-член
AllTracks
по-прежнему будет выделяться, приводя тем самым к созданию 10 000 объектов
Song
в памяти:

using System;

using LazyObjectInstantiation;

Console.WriteLine("***** Fun with Lazy Instantiation *****\n");

// В этом вызывающем коде получение всех композиций не производится,

// но косвенно все равно создаются 10 000 объектов!

MediaPlayer myPlayer = new MediaPlayer;

myPlayer.Play;

Console.ReadLine;

Безусловно, лучше не создавать 10 000 объектов, с которыми никто не будет работать, потому что в результате нагрузка на сборщик мусора .NET Core намного увеличится. В то время как можно вручную добавить код, который обеспечит создание объекта

_allSongs
только в случае, если он применяется (скажем, используя шаблон фабричного метода), есть более простой путь.

Библиотеки базовых классов предоставляют удобный обобщенный класс по имени

Lazy<>
, который определен в пространстве имен
System
внутри сборки
mscorlib.dll
. Он позволяет определять данные, которые не будут создаваться до тех пор, пока действительно не начнут применяться в коде. Поскольку класс является обобщенным, при первом его использовании вы должны явно указать тип создаваемого элемента, которым может быть любой тип из библиотек базовых классов .NET Core или специальный тип, построенный вами самостоятельно. Чтобы включить отложенную инициализацию переменной-члена
AllTracks
, просто приведите код
MediaPlayer
к следующему виду:

// Объект MediaPlayer имеет объект Lazy<AllTracks>.

class MediaPlayer

{

...

private Lazy<AllTracks> _allSongs = new Lazy<AllTracks>;

public AllTracks GetAllTracks

{

// Возвратить все композиции.

return _allSongs.Value;

}

}

Поделиться с друзьями: