Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
С# и объектно-ориентированное программирование.doc
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
1.3 Mб
Скачать
    1. Повторное использование ключевого слова using в с#

При работе с управляемым объектом, который реализует интерфейс IDisposable, довольно часто требуется применять структурированную обработку исключений, гарантируя, что метод Dispose() типа будет вызываться даже в случае возникновения какого-то исключения:

static void Main(string [ ] args)

{

MyResourceWrapper rw = new MyResourceWrapper ();

try

{

// Использование членов rw.

}

finally

{

// Обеспечение вызова метод Dispose() в любом случае,

//в том числе при возникновении ошибки.

rw.Dispose ();

}

}

Хотя это является замечательными примером "безопасного программирования", истина состоит в том, что очень немногих разработчиков прельщает перспектива заключать каждый очищаемый тип в блок try/finally лишь для того, чтобы гарантировать вызов метода Dispose(). Для достижения аналогичного результата, но гораздо менее громоздким образом, в С# поддерживается специальный фрагмент синтаксиса, который выглядит следующим образом:

static void Main(string [ ] args)

{

// Метод Dispose() вызывается автоматически

// при выходе за пределы области действия using.

using(MyResourceWrapper rw = new MyResourceWrapper ())

{

// Использование объекта rw

}

}

Метод Main:

static void Main(string [ ] args)

{

// Метод Dispose() вызывается автоматически

// при выходе за пределы области действия using.

using(MyResourceWrapper rw = new MyResourceWrapper())

{

// Использование объекта rw.

}

}

Если теперь просмотреть CIL-код этого метода Main() с помощью утилиты ildasm.ехе, то обнаружится, что синтаксис using в таких случаях на самом деле расширяется до логики try/finally, которая включает в себя и ожидаемый вызов Dispose():

.method private hidebysig static void Main(string [ ] args) cil managed

{

............

.try

{

} // end .try

finally

{

..........

IL_0012: callvirt instance void

SimpleFinalize. MyResourceWrapper::Dispose()

} // end handler

...........

} // end of method Program::Main

На заметку! При попытке применить using к объекту, который не реализует интерфейс IDisposable, на этапе компиляции возникнет ошибка.

Важно! В настоящее время, к сожалению, ключевое слово using в С# имеет двойное значение (поскольку служит и для добавления ссылки на пространства имен, и для вызова метода Dispose()). Тем не менее, при работе с типами .NET, которые поддерживают интерфейс IDisposable, данная синтаксическая конструкция будет гарантировать автоматический вызов метода Dispose() в отношении соответствующего объекта при выходе из блока using.

    1. Взаимодействие со сборщиком мусора

В библиотеках базовых классов доступен класс по имени System.GC, который позволяет программно взаимодействовать со сборщиком мусора за счет обращения к его статическим членам. Необходимость в непосредственном использовании этого класса в разрабатываемом коде возникает крайне редко (а то и вообще никогда). Обычно единственным случаем, когда нужно применять члены System.GC, является создание классов, предусматривающих использование на внутреннем уровне неуправляемых ресурсов. Это может быть, например, класс, работающий с основанным на С интерфейсом Windows API за счет применения протокола вызовов платформы .NET, или какая-то низкоуровневая и сложная логика взаимодействия с СОМ. В табл. 3 приведено краткое описание некоторых наиболее интересных членов класса System.GC (полные сведения можно найти в документации .NET Framework 4.0 SDK).

Таблица 3.

Член

Назначение

AddMemorуPressure(), RemoveMemoryPressure()

Позволяют указывать числовое значение, отражающее "уровень срочности", который вызывающий объект применяет в отношении к сборке мусора. Следует иметь в виду, что эти методы должны изменять уровень давления в тандеме и, следовательно, никогда не устранять больше давления, чем было добавлено

Collect()

Заставляет сборщик мусора провести сборку мусора. Должен быть перегружен так, чтобы указывать, объекты какого поколения подлежат сборке, а также какой режим сборки использовать (с помощью перечисления GCCollectionMode)

CollectionCount()

Возвращает числовое значение, показывающее, сколько раз объектам данного поколения удалось переживать процесс сборки мусора

GetGeneration()

Возвращает информацию о том, к какому поколению в настоящий момент относится объект

GetTotalMemory()

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

MaxGeneration

Возвращает информацию о том, сколько максимум поколений поддерживается в целевой системе. В .NET 4.0 поддерживается всего три поколения: 0, 1 и 2

SuppressFinalize()

Позволяет устанавливать флаг, указывающий, что для данного объекта не должен вызываться его метод Finalize()

WaitForPendingFinalizers()

Позволяет приостанавливать выполнение текущего потока до тех пор, пока не будут финализированы все объекты, предусматривающие финализацию. Обычно вызывается сразу же после вызова метода GC.Collect()

Рассмотрим применение System.GC для получения касающихся сборки мусора деталей на примере следующего метода Main(), в котором используются сразу несколько членов System.GC:

static void Main(string [ ] args)

{

// Вывод подсчитанного количества байтов в куче.

Console.WriteLine("Estimates bytes on heap: {0}",

GC.GetTotalMemory(false));

// Отсчет для MaxGeneration начинается с нуля,

//поэтому для удобства добавляется 1.

Console.WriteLine("This OS has @} object generations.\n",

(GC.MaxGeneration + 1));

Car refToMyCar = new Car("Zippy", 100);

Console.WriteLine(refToMyCar.ToString());

// Вывод информации о поколении объекта refToMyCar.

Console.WriteLine("Generation of refToMyCar is: @ 1",

GC.GetGeneration(refToMyCar));

}