Урок посвящен понятиям класса и объекта в C#, а также ссылочным и значимым типам данных
Понятия класса и объекта
Рассмотрим два очень важных понятия в C#, без которых будет очень сложно двигаться дальше. В рамках этого степа мы попытаемся дать интуитивное представление об этих понятиях. Обо всех (почти) тонкостях вы узнаете в соответствующем модуле данного курса.
Класс можно для себя представить как чертеж, некоторое абстрактное представление сущности. С помощью классов мы моделируем различные понятия или группы понятий из реального мира, например, Автомобиль, Собака, Стол, Сервер, Сокет, Студент и т. п.
Класс – это некоторое описание того, какую информацию должен содержать его экземпляр. Точно так же как чертеж используется для создания какого-либо механизма, устройства или конструкции, класс используется для создания объектов.
Для объявления класса в C# используется ключевое слово class
, перед ним указываются модификаторы доступа и ряд других ключевых слов, которые определяют его свойства (статический, абстрактный и т. п.).
Например, создадим простой класс Person
.
class Person { public string Name {get; set;} public int Age {get; set;} }
У этого класса два свойства: Имя – Name
и Возраст – Age
, значения которых можно считывать и изменять.
Пока от вас не требуется понимания смысла всех ключевых слов типа public
, get
, set
и т. п. Постарайтесь понять его на уровне концепции.
Используя этот класс (модель), мы можем создавать его экземпляры, то есть сущности, которые будут иметь структуру, как у класса, но каждый из них может иметь свои уникальные значения полей.
var p1 = new Person() { Name = "John", Age = 21 }; var p2 = new Person() { Name = "Mary", Age = 25 };
В примере, p1
и p2
– это объекты класса Person
.
Выведем на консоль содержимое их полей:
// p1: Name: John, Age: 21 Console.WriteLine($"p1: Name: {p1.Name}, Age: {p1.Age}"); // p2: Name: John, Age: 25 Console.WriteLine($"p2: Name: {p1.Name}, Age: {p1.Age}");
Ссылочные типы и значимые типы
То, о чем мы с вами будет говорить на этом шаге, может показаться на первый взгляд сложным. Если сразу не стало всё понятно, вернитесь к этой информации чуть позже.
Типы данных в C# можно разделить на типы-значения (value type) и ссылочные типы (reference type). В первую очередь они отличаются схемой наследования, местом размещения и представлением.
Для начала стоит сказать о том, что такое стек и управляемая куча. Стек – это область памяти процесса, особенность которой состоит в том, что участки из нее выделяются по принципу LIFO (last in – first out, последним пришёл – первым вышел) – первым доступным элементом является тот, что был помещен последним. Аналогией является стопка тарелок, в которой, для того чтобы добраться до какой-то из них, вам нужно снять сверху все те, что мешают достать нужную.
Данные из стека удаляются предсказуемым образом, например, после завершения работы функции, все, что было размещено в стеке в процессе ее работы, будет уничтожено.
Пусть у нас есть три элемента данных, которые хранятся в стеке:
Если мы хотим добавить в него ещё два элемента 4 и 5, то это мы сможет сделать, только разместив их сверху, по очереди:
Куча – это также область памяти процесса, но в отличие от стека, в ней нет жесткой структуры хранения. За уничтожение объектов, которые в ней размещены, в .NET отвечает сборщик мусора (garbage collector). В таких языках, как C / C++ за этим должен следить сам разработчик.
Все типы в C#, как мы говорили выше, являются наследниками от System.Object
. Все типы-значения, помимо этого, являются наследниками ValueType
.
При создании экземпляров ссылочного типа в куче выделяется место, создаются и инициализируются дополнительные поля. В какой-то момент, занимаемая объектом память будет освобождена сборщиком мусора. Это произойдет в случае, если на него (объект) не будет ссылаться никакие другие объекты внутри приложения.
Если бы все типы в языке C# были бы ссылочными типами, то производительность приложений во многих случаях значительно пострадала из-за того, что для каждого экземпляра типа нужно выделять память в куче, создать и инициализировать дополнительные поля, а потом удалять объекты при сборке мусора.
Для часто используемых типов предусмотрена отдельная подгруппа, которую называют типы-значения (value type). Их особенностью является то, что экземпляры этих типов располагаются в стеке, что позволяет их быстро создавать и уничтожать. Фактически время жизни такой переменной определяется контекстом, в котором она объявлена. Из-за этой особенности снижается нагрузка на кучу и сборщик мусора. Последний просто не вызывается для экземпляров типов-значений.
Следует заметить, что если экземпляр типа-значения (например, int
) является составляющей частью какого-то ссылочного типа, то он будет располагаться в куче.
Типы-значения являются классами наследниками от System.ValueType
, который, в свою очередь, наследуется от System.Object
. От типов-значений нельзя наследоваться.
Более подробно про эти типы мы поговорим в двух ближайших уроках этого модуля.
Общая структура схемы наследования типов в C# представлена на рисунке ниже. Сразу скажем, что это схема неполная, но она дает представление об устройстве наследования типов.
Если Вы хотите больше узнать про язык C#, приглашаем Вас на наш курс “C#. Базовый уровень“.