- •Лекция 2. С# и объектно-ориентированное программирование Формальное определение класса в с#
- •Ссылки на самого себя
- •Определение открытого интерфейса по умолчанию
- •Указание области видимости на уровне типа: открытые и внутренние типы
- •Столпы объектно-ориентированного программирования
- •Инкапсуляция
- •Наследование: отношения «быть» и «иметь»
- •Полиморфизм: классический и для конкретного случая
- •Средства инкапсуляции в с#
- •Реализация инкапсуляции при помощи традиционных методов доступа и изменения
- •Второй способ инкапсуляции: применение свойств класса
- •Поддержка наследования в с#
- •Применение модели включения-делегирования
- •Поддержка полиморфизма в с#
- •Абстрактные классы
- •Принудительный полиморфизм: абстрактные методы
- •Приведение типов в с#
- •Приведение числовых типов
- •Обработка исключений
- •Жизненный цикл объектов
- •Завершение ссылки на объект
- •Интерфейс iDisposable
- •Взаимодействие со сборщиком мусора
Завершение ссылки на объект
Та схема управления памятью, которая была рассмотрена выше, обладает одной важной особенностью, которая имеет как положительные, так и отрицательные стороны: она работает полностью автоматически. С одной стороны, это упрощает процесс программирования. С другой — нас может не устраивать то, что процесс удаления объектов (закрытия соединения с базой данных, окна Windows и т. п.) будет происходить в соответствии с неизвестным нам алгоритмом. Например, если тип Саг устанавливает в процессе выполнения соединение с удаленным компьютером, скорее всего, мы захотим, чтобы эго соединение разрывалось в соответствии с установленными нами правилами.
Если вам нужно обеспечить возможность удаления объектов из оперативной памяти в соответствии с определенными вами правилами, первое, о чем вам необходимо позаботиться — о реализации в вашем классе метода System.Object.Finаlize( ). Заметим между прочим, что реализация этого метода по умолчанию (в базовом классе) ничего не дает. Однако, как это ни странно, в С# запрещено напрямую замещать метод System.Object.Finаlize( ). Более того, вы даже не сможете вызвать в вашем приложении этот метод напрямую! Если вы хотите, чтобы ваш пользовательский класс поддерживал метод Finalize( ), вы должны использовать в определении этого класса метод, очень похожий на деструктор C++:
// Что-то очень знакомое
public class Car : Object
{
// Деструктор С#?
~Саг( )
{
// Закрывайте все открытые объектом ресурсы!
// Далее в С# будет автоматически вызван метод Base FinalizeO
}
}
Если у вас есть опыт работы с C++, то подобный синтаксис вам покажется знакомым. В C++ деструктор класса — это специальный метод класса, имя которого выглядит как имя класса, перед которым стоит символ тильды (~). В C++ гарантируется, что этот метод будет вызван, когда ссылка на объект выходит за пределы области видимости (для типов, размещенных в стеке) или к объекту применяется оператор delete (для объектов, размещенных в области динамической памяти).
При размещении объекта С# в управляемой куче при помощи оператора new среда выполнения автоматически определяет, поддерживает ли ваш объект метод Finalize( ) (представленный в С# с помощью «деструктороподобного» синтаксиса). Если этот метод поддерживается объектом, ссылка на этот объект помечается как «завершаемая» (finalizable). При этом в специальной внутренней очереди «завершения» (finalization queue) помещается указатель на данный объект. Когда сборщик мусора приходит к выводу, что наступило время удалять данный объект из оперативной памяти, он обращается к этому указателю и запускает деструктор С#, определенный для этого класса, прежде чем будет произведено физическое удаление объекта из памяти.
Интерфейс iDisposable
Чтобы обеспечить единообразие методов, освобождающих ресурсы в разных клас-ах, библиотеки классов .NET определяют интерфейс IDisposable, который содержит единственный член — метод Dispose( ):
public interface IDisposable
{
public void Dispose();
}
С концепцией интерфейсов мы познакомимся в следующей главе. Сейчас достаточно будет отметить, что рекомендуется реализовывать этот интерфейс для всех -классов, которые должны поддерживать явную форму освобождения ресурсов. Например, применить этот интерфейс в случае класса Саг можно следующим образом:
// Реализуем IDisposable
public Car : IDisposable
{
// Это - единственный метод, который пользователь объекта должен вызвать
// вручную
public void Dispose( )
{
// ...Закрываем открытые внутренние ресурсы
}
}
Используя этот подход, мы предоставляем пользователю возможность в любой момент высвобождать наиболее ценные ресурсы, не загружая дополнительно оче-эедь завершения. Кроме того, мы вполне можем сочетать применение интерфейса .Disposable (с методом Dispose( )) и пользовательского деструктора С#.