Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Базовые технологии платформы .NET.pdf
Скачиваний:
65
Добавлен:
11.05.2015
Размер:
1.81 Mб
Скачать

Существует абстрактный класс System.Collections.Generic.Comparer<T>,

облегчающий реализацию интерфейсов IComparer<T> и IComparer:

public class Wish

{

public string Name { get; set; } public int Priority { get; set; }

}

public class PriorityComparer : Comparer<Wish>

{

public override int Compare(Wish x, Wish y)

{

if (Equals(x, y))

{

return 0;

}

return x.Priority.CompareTo(y.Priority);

}

}

Статическое свойство Comparer<T>.Default возвращает экземпляр Comparer<T> для типа T. Этот экземпляр использует метод CompareTo(T) либо метод CompareTo(object) в зависимости от того, реализует ли тип T интерфейс

IComparable<T>.

Абстрактный класс System.StringComparer предназначен для сравнения строк с учётом языка и регистра. Этот класс реализует интерфейсы IComparer,

IEqualityComparer, IComparer<string> и IEqualityComparer<string>. Некоторые

неабстрактные наследники StringComparer доступны через его статические свойства. Например, свойство StringComparer.CurrentCulture возвращает объект, реализующий сравнение строк с учётом текущих региональных стандартов.

Интерфейс System.Collections.IStructuralComparable позволяет провести

порядковое сравнение составных объектов, выполняя сравнение отдельных элементов этих объектов. На платформе .NET этот интерфейс явно реализуют массивы и кортежи.

public interface IStructuralComparable

{

int CompareTo(object other, IComparer comparer);

}

7. Жизненный цикл объектов

Все типы платформы .NET делятся на ссылочные типы и типы значений. Переменные типов значений создаются в стеке, их время жизни ограничено тем блоком кода, в котором они объявляются. Например, если переменная типа значения объявлена в некотором методе, то после выхода из метода память в стеке, занимаемая переменной, автоматически освободится. Переменные ссылочного

21

типа (объекты) располагаются в динамической памяти – управляемой куче (managed heap). Размещение объектов в управляемой куче происходит последовательно. Для этого CLR поддерживает указатель на свободное место в куче, перемещая его на соответствующее количество байтов после выделения памяти очередному объекту.

1)

A

B

C

D

свободная память

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NextObjPtr

2)

A

B

C

D

E

свободная память

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NextObjPtr

Рис. 1. Управляемая куча до (1) и после (2) выделения памяти под объект E.

7.1. Алгоритм сборки мусора

Если новый объект требует для размещения больше памяти, чем имеющийся свободный объем, CLR запускает процесс, называемый сборка мусора1 (garbage collection). На первом этапе сборки мусора строится граф используемых объектов. Отправными точками в построении графа являются корневые объекты. Это объекты следующих категорий:

локальная переменная или аргумент выполняемого метода (а также всех методов в стеке вызова);

статическое поле;

объект в очереди завершения (этот термин будет разъяснён позже).

При помощи графа используемых объектов выясняется реально занимаемая этими объектами память. Затем происходит дефрагментация кучи – используемые объекты перераспределяются так, чтобы занимаемая ими память составляла единый блок в начале кучи. После этого сборка мусора завершается, и новый объект размещается в управляемой куче.

1 Компилятор C# генерирует код, принудительно запускающий сборку мусора при окончании работы программы.

22

1)

A

B

C

D

E

F

 

 

 

 

 

 

 

 

 

 

 

 

NextObjPtr

2)

A

B

C

D

E

свободная память

3)

A

C

свободная память

 

 

 

 

4)

A

C

F

свободная память

 

 

 

 

 

NextObjPtr

Рис. 2. Фазы сборки мусора: 1) инициирование, 2) выявление используемых объектов, 3) дефрагментация, 4) размещение объекта.

При размещении и удалении объектов CLR использует ряд оптимизаций. Во-первых, объекты размером 85000 и более байтов размещаются в отдельной управляемой куче больших объектов (Large Object Heap). При сборке мусора данная куча не дефрагментируется, так как копирование больших блоков памяти снижает производительность. Во-вторых, в управляемой куче для малых объектов выделяется три поколения – Gen0, Gen1 и Gen2. Вначале все объекты относятся к поколению Gen0. После первой сборки мусора «пережившие» её объекты переходят в поколение Gen1. В дальнейшем сборка мусора будет работать с объектами поколения Gen1, только если освобождение памяти в Gen0 дало неудовлетворительный результат. Если в Gen1 произошла сборка мусора, то «пережившие» её объекты переходят в поколение Gen2. Отметим, что куча больших объектов всегда рассматривается как куча объектов поколения Gen2.

Сборщик мусора представлен статическим классом System.GC, который обладает несколькими полезными методами (приведён неполный список):

1.Collect() – вызывает принудительную сборку мусора в программе.

2.GetGeneration() – возвращает номер поколения для указанного объекта;

3.SuppressFinalize() – подавляет вызов финализатора для объекта;

4.WaitForPendingFinalizers() – приостанавливает текущий поток выпол-

нения до тех пор, пока не будут выполнены все финализаторы освобождаемых объектов.

23

7.2. Финализаторы и интерфейс IDisposable

Обсудим автоматическую сборку мусора с точки зрения программиста, разрабатывающего некий класс. С одной стороны, такой подход имеет свои преимущества. В частности, практически исключаются случайные утечки памяти, которые могут вызвать «забытые» объекты. Однако объект может в процессе создания или работы резервировать неуправляемые системные ресурсы, которые нужно освободить после того, как объект перестаёт использоваться. Примерами таких ресурсов являются открытый операционной системой файл или сетевое подключение. Платформа .NET предлагает подход, обеспечивающий автоматическое освобождение ресурсов при уничтожении объекта. Тип System.Object содержит виртуальный метод Finalize(). Класс (но не структура!) может переопределить этот метод для освобождения неуправляемых ресурсов. Объект такого класса обрабатывается особо при размещении в куче и при сборке мусора. При размещении ссылка на объект запоминается в специальной внутренней структуре CLR. При сборке мусора эта ссылка перемещается в очередь завершения (freachable queue). Затем в отдельном программном потоке у объектов из очереди завершения происходит вызов метода Finalize(), после этого ссылка на объект удаляется из очереди завершения.

Язык C# не позволяет явно переопределить в пользовательском классе метод Finalize(). Вместо этого в классе описывается специальный финализатор. Имя финализатора имеет вид ~имя-класса(), он не имеет параметров и модификаторов доступа (считается, что у него модификатор доступа protected). При наследовании в финализатор класса-наследника автоматически подставляется вызов финализатора класса-предка.

Приведём пример класса с финализатором:

public class ClassWithFinalizer

{

public void DoSomething()

{

Console.WriteLine("I am working...");

}

~ClassWithFinalizer()

{

// здесь должен быть код освобождения неуправляемых ресурсов

Console.WriteLine("Bye!");

}

}

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

24