Основы C#: часть II…
Февраль 19, 2007 — seregaborzovКлассы и структуры
У класса две различные роли: модуля и типа данных. Класс — это модуль, архитектурная единица построения программной системы. Модульность построения — основное свойство программных систем. Модуль может не представлять собой содержательную единицу — его размер и содержание определяется архитектурными соображениями, а не семантическими.
Вторая роль класса не менее важна. Класс — это тип данных, задающий реализацию некоторой абстракции данных, характерной для задачи, в интересах которой создается программная система. С этих позиций классы — не просто кирпичики, из которых строится система. Каждый кирпичик теперь имеет важную содержательную начинку.
В теле класса могут быть объявлены:
• константы;
• поля;
• конструкторы и деструкторы;
• методы;
• события;
• делегаты;
• классы (структуры, интерфейсы, перечисления).
C# является языком объектно-ориентированного программирования, поэтому классы играют в нем основополагающую роль. Более того, все типы данных C#, как встроенные, так и определенные пользователем, порождены от базового класса object. Иными словами, в отличие от Java, где примитивные типы данных отделены от объектных типов, все типы данных в C# являются классами и могут быть разделены на две группы:
ссылочные (reference types);
обычные (value types).
Внешне ссылочные и обычные типы очень похожи, так как аналогично Cи++ в них можно объявлять конструкторы, поля, методы, операторы и т.д. Однако, в отличие от Cи++, обычные типы в C# не позволяют определять классы и не поддерживают наследования. Они описываются с помощью ключевого слова struct и в основном используются для создания небольших объектов. Ссылочные же типы описываются с помощью ключевого слова class и являются указателями, а экземпляры таких типов ссылаются на объект, находящийся в куче (heap). Продемонстрируем сказанное на примере:
using System; class CValue { public int val; public CValue(int x) {val = x;} } class Example_1 { public static void Main() { CValue p1 = new CValue(1); CValue p2 = p1; Console.WriteLine(”p1 = {0}, p2 = {1}”, p1.val, p2.val); p2.val = 2; Console.WriteLine(”p1 = {0}, p2 = {1}”, p1.val, p2.val); } }
Откомпилировав и выполнив программу, получим следующий результат:
p1 = 1, p2 = 1
p1 = 2, p2 = 2
Как нетрудно видеть, p2 является всего лишь ссылкой на p1. Тем самым становится очевидно, что при изменении поля val экземпляра класса p2 в действительности изменяется значение соответствующего поля p1. Подобный подход не очень удобен при работе с примитивными типами данных, которые должны содержать само значение, а не ссылку на него (Complex, Point, Rect, FileInfo и т.д.). Для описания таких объектов и предназначены типы значений:
using System; struct SValue { public int val; public SValue(int x) {val = x;} } class Example_2 { public static void Main() { SValue p1 = new SValue(1); SValue p2 = p1; Console.WriteLine(”p1 = {0}, p2 = {1}”, p1.val, p2.val); p2.val = 2; Console.WriteLine(”p1 = {0}, p2 = {1}”, p1.val, p2.val); } }
Вот что получится после запуска вышеприведенной программы:
p1 = 1, p2 = 1
p1 = 1, p2 = 2
Из этого следует, что экземпляр класса p2 является самостоятельным объектом, который содержит собственное поле val, не связанное с p1. Использование обычных типов позволяет избежать дополнительного расходования памяти, поскольку не создаются дополнительные ссылки, как в случае с экземплярами классов. Конечно, экономия невелика, если у вас имеется всего несколько небольших объектов типа Complex или Point. Зато для массива, содержащего несколько тысяч таких элементов, картина может в корне измениться. В таблице приведены основные отличия типов class и struct:
| Тип class | Тип struct | |
| Представление экземпляра типа | указатель | значение |
| Местоположение объекта | куча | стек |
| Значение по умолчанию | null | заполняется нулями |
| Результат операции присваивания для экземпляров типа | копируется указатель | копируется сам объект |
| Базовый тип | встроенный тип string | встроенный тип int |
продолжение следует…



