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

<p id="AutBody_Root368">Ленивое создание объектов</p>

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

В качестве примера предположим, что строится класс, который инкапсулирует операции цифрового музыкального проигрывателя. В дополнение к ожидаемым методам вроде 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 только в случае, если он применяется (скажем, используя шаблон фабричного метода), есть более простой путь.

Перейти на страницу:

Похожие книги