Классы
13.1. Классы: основные понятия, данные, методы, конструкторы, свойства
Классы
Основные понятия
Класс - это обобщенное понятие, определяющие характеристики и поведение некоторого множества объектов, называемых экземплярами класса. "Классический" класс содержит данные, определяющие свойства объектов класса, и методы, определяющие их поведение. Для Windows-приложений в класс добавляется третья составляющая - события, на которые может реагировать объект класса. Все классы библиотеки .Net, а также все классы, которые создает программист в среде .Net, имеют одного общего предка - класс object.
Все программы, рассмотренные ранее, состояли из одного класса с одним методом Main и несколькими вспомогательными статическими методами. Теперь рассмотрим понятие "класс" более подробно.
Описание класса содержит ключевое слово class, за которым следует его имя, а далее в фигурных скобках - тело класса. Кроме того, для класса можно задать его базовые классы (предки) и ряд необязательных атрибутов и спецификаторов, определяющих различные характеристики класса:
[ атрибуты ] [ спецификаторы ] class имя_класса [ : предки ] {тело_класса}
Простейший пример класса:
class Demo{}
Спецификаторы определяют свойства класса, а также доступность класса для других элементов программы. Возможные значения спецификаторов перечислены в следующей таблице:
№ | Спецификатор | Описание |
---|---|---|
1 | new | Задает новое описание класса взамен унаследованного от предка. Используется для вложения классов (в иерархии объектов). |
2 | public | Доступ к классу не ограничен |
3 | protected | Доступ только из данного или производного класса. Используется для вложенных классов. |
4 | internal | Доступ только из данной программы (сборки). |
5 | protected internal | Доступ только из данного и производного класса и из данной программы (сборки). |
6 | private | Доступ только из элементов класса, внутри которых описан данный класс. Используется для вложенных классов. |
7 | static | Статический класс. Позволяет обращатся к методам класса без создания экземпляра класса |
8 | sealed | Бесплодный класс. Запрещает наследование данного класса. Применяется в иерархии объектов. |
9 | abstract | Абстрактный класс. Применяется в иерархии объектов. |
Спецификаторы 2-6 называются спецификаторами доступа. Они определяют, откуда можно непосредственно обращаться к данному классу. Спецификаторы доступа могут комбинироваться с остальными спецификаторами.
Класс можно описывать непосредственно внутри пространства имен или внутри другого класса. В последнем случае класс называется вложенным. В зависимости от места описания класса некоторые из этих спецификаторов могут быть запрещены. В данном разделе мы рассмотрим классы, которые описываются непосредственно в пространстве имен. Для таких классов допускаются только два спецификатора: public и internal. По умолчанию, то есть если ни один спецификатор доступа не указан, подразумевается спецификатор internal.
Объекты создаются явным или неявным образом, то есть либо программистом, либо системой. Программист создает экземпляр класса с помощью операции new, например:
Demo a = new Demo (); // Создается экземпляр класса Demo
Если достаточный для хранения объекта объем памяти выделить не удалось, то генерируется исключение OutOfMemoryException.
Для каждого объекта при его создании в памяти выделяется отдельная область, в которой хранятся его данные. В классе могут присутствовать статические элементы, которые существуют в единственном экземпляре для всех объектов класса. Статические данные часто называют данными класса, а остальные - данными экземпляра. Для работы с данными класса используются статические методы класса, для работы с данными экземпляра - методы экземпляра, или просто методы.
До сих пор мы использовали в программах только данные (переменные и константы) и методы. В общем случае класс может содержать следующие функциональные элементы:
- Данные: переменные или константы.
- Методы, реализующие не только вычисления, но и другие действия, выполняемые классом или его экземпляром.
- Конструкторы (реализуют действия по инициализации экземпляров или класса в целом).
- Свойства (определяют характеристики класса в соответствии со способами их задания и получения).
- Деструкторы (определяют действия, которые необходимо выполнить до того, как объект будет уничтожен).
- Индексаторы (обеспечивают возможность доступа к элементам класса по их порядковому номеру).
- Операции (задают действия с объектами с помощью знаков операций).
- События (определяют уведомления, которые может генерировать класс).
- Типы (типы данных, внутренние по отношению к классу).
В данном разделе мы рассмотрим первые четыре категории элементов класса
Прежде чем приступить к проектированию классов, необходимо поговорить о присваивании и сравнении объектов. Механизм выполнения присваивания один и тот же для величин любого типа, как ссылочного, так и размерного, однако результаты различаются. При присваивании значения копируется значение, а при присваивании ссылки - ссылка, поэтому после присваивания одного объекта другому мы получим две ссылки, указывающие на одну и ту же область памяти:
Пусть были созданы три объекта а, b и с, а затем выполнено присваивание b = с. Теперь ссылки b и с указывают на один и тот же объект. Старое значение b становится недоступным и очищается сборщиком мусора.
Аналогичная ситуация с операцией проверки на равенство. Величины значимого типа равны, если равны их значения. Величины ссылочного типа равны, если они ссылаются на одни и те же данные. Так, объекты b и с равны, т.к. они ссылаются на одну и ту же область памяти. Но а не равно b даже при равенстве их значений.
Данные: поля и константы
Данные, содержащиеся в классе, могут быть переменными или константами и задаются в соответствии с правилами, рассмотренными в теме "Идентификаторы". При описании данных также можно указывать атрибуты и спецификаторы, задающие различные характеристики элементов. Синтаксис описания элемента данных приведен ниже:
[атрибуты] [спецификаторы] [const] тип имя [ = начальное_значение ]
Рассмотрим возможные спецификаторы для данных:
Для констант можно использовать только спецификаторы 1-6.
По умолчанию элементы класса считаются закрытыми private. Для полей класса этот вид доступа является предпочтительным, поскольку поля определяют внутреннее строение класса, которое должно быть скрыто от пользователя. Все методы класса имеют непосредственный доступ к его закрытым полям.
Поля, описанные со спецификатором static, а также константы существуют в единственном экземпляре для всех объектов класса, поэтому к ним обращаются не через имя экземпляра, а через имя класса. Обращение к полю класса выполняется с помощью операции доступа (точка). Справа от точки задается имя поля, слева - имя экземпляра для обычных полей или имя класса для статических. Рассмотрим пример создания класса Demo и два способа обращения к его полям.
class Circle { public int x=0; public int y=0; public int radius=3; public const double pi = 3.14; public static string name = "Окружность"; double p; double s; } class Program { static void Main() { Circle cr = new Circle(); //создание экземпляра класса Console.WriteLine("pi=" + Circle.pi);// обращение к константе Console.Write(Circle.name);// обращение к статическому полю //обращение к обычным полям Console.WriteLine(" с центром в точке ({0},{1}) и радиусом {2}", cr.x, cr.y, cr.radius); // Console.WriteLine(cr.p); - вызовет ошибку, т.к. поле p имеет тип private Console.Write("Введите коэффициент= "); int kof = int.Parse(Console.ReadLine()); cr.x -= kof; cr.y += kof; cr.radius *= kof; Console.WriteLine(" Новая окружность с центром в точке ({0},{1}) и радиусом {2}", cr.x, cr.y, cr.radius); //cr.s = 2 * Circle.pi * cr.radius; - вызовет ошибку, т.к. поле s имеет тип private } }
Методы
Методы находятся в памяти в единственном экземпляре и используются всеми объектами одного класса совместно, поэтому необходимо обеспечить работу методов нестатических экземпляров с полями именно того объекта, для которого они были вызваны. Для этого в любой нестатический метод автоматически передается скрытый параметр this, в котором хранится ссылка на вызвавший функцию экземпляр.
В явном виде параметр this применяется для того, чтобы возвратить из метода ссылку на вызвавший объект, а также для идентификации поля в случае, если его имя совпадает с именем параметра метода, например:
class Circle { public int x=0; public int y=0; public int radius=3; public const double pi = 3.14; public static string name = "Окружность"; public Circle T() //метод возвращает ссылку на экземпляр класса { return this; } public void Set(int x, int y, int r) { this.x = x; this.y = y; radius=r; } } class Program { static void Main() { Circle cr = new Circle(); //создание экземпляра класса Console.WriteLine("pi=" + Circle.pi);// обращение к константе Console.Write(Circle.name);// обращение к статическому полю //обращение к обычным полям Console.WriteLine(" с центром в точке ({0},{1}) и радиусом {2}", cr.x, cr.y, cr.radius); cr.Set(1, 1, 10); Console.WriteLine("Новая окружность с центром в точке ({0},{1}) и радиусом {2}", cr.x, cr.y, cr.radius); Circle b=cr.T();//получаем ссылку на объект cr, аналог b=c Console.WriteLine("Новая ссылка на окружность с центром в точке ({0},{1}) и радиусом {2}", b.x, b.y, b.radius); } }