Содержание
Язык C# является объектно-ориентированным языком программирования. Это значит, что любую программу (проект) на языке C# можно представить в виде одного или нескольких сущностей, которые могут взаимодействовать между собой. Понимание того, как объявить класс и наделить его определенными свойствами, создать и управлять объектами в C# можно считать одними из главных задач при изучении этого языка программирования. До сих пор мы хоть и использовали в изучении основ C# классы и объекты, но особо и не задумывались об этом, так как всё наше внимание было уделено изучению отдельных типов данных, которые можно назвать примитивными. Сегодня же мы начнем разбираться с такими пользовательскими типами данных как классы и научимся создавать пусть и простые, но объекты C#.
Класс и объект: в чем отличие?
Самое легкое объяснение того, в чем отличие класса от объекта можно сформулировать следующим образом: «Описанием объекта является класс, а объект представляет экземпляр этого класса«. Проще не придумать. Но это, если иметь хотя бы малейшее представление о программировании, а если таковых представлений нет?
В свое время, для самого себя я провел следующую аналогию между классами и объектами в программировании и материальным миром: есть чертеж, например, дома, а есть сам дом. Чертеж может содержать описание свойств дома (длина, ширина, высота и т.д.). Так вот, чертеж — это класс, а дом — это объект (реальная сущность, которую можно измерить, потрогать и так далее). По одному и тому же чертежу (классу) мы можем создать сколько угодно домов (объектов). Конечно, это очень упрощенная аналогия, не в полной мере описывающая что такое класс,а что такое объект, но, тем не менее, для начала изучения классов и объектов нам этого должно хватить.
Описание класса в C#
Класс в C# -это пользовательский тип данных. До сих пор мы оперировали встроенными типами данных типа целых чисел, массивов и так далее. Сейчас же мы будем создавать собственный тип данных. В самом общем случае, для описания класса в C# используется следующая конструкция:
class Имя_класса { свойства, методы класса и т.д. }
Ключевым словом class
мы указываем на то, что создаем тип данных — класс. Далее идёт имя класса, а в фигурных скобках указываются поля, свойства, методы класса и прочие интересные вещи с которыми мы будем постепенно знакомиться.
Где можно размещать классы? Вообще, в C# нет чёткого требования к тому где и как размещать новые классы — вы можете размещать их в пространстве имён, вне пространства имен в отдельных или в одном файле программы и так далее. Однако, по негласному правилу и для дальнейшего удобства поддержки вашего проекта, рекомендуется размещать каждый класс в отдельном файле с названием, соответствующим названию класса. Давайте создадим наш первый класс и посмотрим как с ним можно работать.
Первый класс в C#
Раз уж в начале шла речь про чертежи и дома, то пусть наш первый класс и будет описаться дом. Создаем новый проект консольного приложения C#. Перед нами откроется, как обычно, редактор кода с открытым файлом Program.cs
:
// See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!");
Или, если не использовать операторы верхнего уровня, то так:
using System; namespace FirstClass { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }
Своё приложение мы назвали FirstClass
. Теперь мы могли бы объявить наш новый класс прямо в этом файле, но я бы рекомендовал сразу привыкать к вышеизложенному правилу: 1 файл = 1 класс, поэтому кликаем правой кнопкой мыши по названию проекта в «Обозревателе решений» Visual Studio и выбираем пункт меню «Добавить — Создать элемент»:
В появившемся окне выбираем первый пункт — «Класс» и в самом низу окна задаем название файла. Например, я дал файлу название «Building.cs»
Нажимаем кнопку «Добавить» и Visual Studio создаст нам новый файл следующего содержания:
using System; using System.Collections.Generic; using System.Text; namespace FirstClass { class Building { } }
Обратите внимание, что, по умолчанию, наш новый класс получил имя по имени файла (Building
) и размещен он в том же пространстве имен, что и класс Program
нашего приложения. Теперь нам необходимо наделить наш класс определенной функциональностью.
Вся функциональность класса представляется его членами — полями, свойствами, методами и событиями. О каждом из членов класса мы поговорим подробнее, а сегодня воспользуемся только двумя из них — полями и методами.
Поле класса — это переменная, описание которой мы даем при создании класса. Например, здание (Building
) имеет свою ширину, длину и высоту. Создадим три поля:
class Building { double width; double length; double height; }
Теперь наш класс содержит три поля, в которых будут храниться данные о нашем здании.
Будем считать, что все здания имеют прямоугольную форму, напишем метод, позволяющий рассчитать объем нашего здания:
internal class Building { double width; double length; double height; public double GetVolume() => width * length * height; }
Так как в методе GetVolume()
используется всего одна инструкция, то мы использовали сокращенную запись метода. Итак, мы создали новый тип данных — класс Building
. Для того, чтобы использовать возможности этого класса в своем приложении, мы должны создать переменную этого типа (объект) и инициализировать её. Для инициализации объектов используются специальные методы — конструкторы.
Конструкторы
Конструкторы вызываются при создании нового объекта и используются для его (объекта) инициализации. По умолчанию любой класс C# имеет хотя бы один конструктор.
Конструктор по умолчанию
Конструктор по умолчанию не имеет ни параметров ни тела. Если вы при описании класса не создаете свой конструктор, то при создании объекта используется конструктор по умолчанию. Например, создадим объект здания, используя конструктор по умолчанию. Для этого, кликните в обозревателе решений левой кнопкой мыши по файлу «Program.cs» и напишите следующий код:
using FirstClass; Building building = new Building(); Console.WriteLine($"Объем здания: {building.GetVolume()}");
Здесь мы объявили переменную (объект) building
типа Building
(нашего класса) и инициализировали её, используя конструктор по умолчанию. Чтобы создать объект мы использовали ключевое слово new
(этот оператор выделяет память для нового объекта) и для вызова конструктора поставили после названия класса круглые скобки. После этого,переменная building
будет содержать ссылку на объект в памяти. Во второй строке мы вывели в консоль объем нового здания. Ну, а так как по умолчанию у нас все переменные вещественного типа инициализируются значением 0
, то при запуске приложения мы увидим в консоли вот такую запись:
Собственные конструкторы
Для класса в C# можно создать любое количество своих конструкторов. Например, создадим такой конструктор для нашего класса:
internal class Building { double width; double length; double height; public double GetVolume() => width * length * height; //Конструктор public Building(double width, double length, double height) { this.width = width; this.length = length; this.height = height; } }
Обратите внимание, что название конструктора должно полностью совпадать с названием класса. В остальном, конструктор ничем не отличается от методов C# о которых мы уже говорили. В нашем конструкторе использовалось ещё одно новое для нас ключевое слово — this
, которое представляет ссылку на текущий экземпляр класса. Это ключевое слово может использоваться для различных нужд, но, конкретно в данном случае я использовал this
потому что имена параметров у конструктора полностью совпадают с именами полей класса. Если бы я не использовал this
, то компилятор не понял, что я хочу сделать (присвоить полю класса значение параметра метода) и предложил бы избавиться от лишнего присваивания:
После того как вы создадите свой конструктор конструктор по умолчанию перестанет использоваться и вы увидите в списке ошибок в Visual Studio следующую ошибку:
Наш конструктор должен содержать три обязательных параметра и теперь создать объект класса мы можем, например, так:
Building building = new Building(20, 20, 4);
Как и в любом методе в C#, мы можем определить в конструкторе необязательные параметры. Например, так:
public Building(double width, double length, double height = 3) { this.width = width; this.length = length; this.height = height; }
И теперь два следующих объекта будут абсолютно идентичны:
static void Main(string[] args) { Building building = new Building(20, 20, 3); Building building2 = new Building(20, 20); Console.WriteLine($"Объем первого здания: {building.GetVolume()}"); Console.WriteLine($"Объем второго здания: {building2.GetVolume()}"); }
Вывод консоли:
Объем второго здания: 1200
Начиная с версии C# 12 и .NET 8 появилась ещё одна возможность — использование основного конструктора. Чтобы понять как выглядит такой конструктор, ниже представлен код нашего класса Building:
internal class Building(double width, double length, double height) { double width = width; double length = length; double height = height; public double GetVolume() => width * length * height; }
Сразу после названия класса в круглых скобках указываются параметры основного конструктора и, при этом, параметры такого конструктора доступны в любом месте класса. Как можно видеть по представленному выше коду, мы использовали параметры основного конструктора непосредственно для инициализации полей класса и, обратите внимание — нам не потребовалось указывать ключевое слово this
.
Ключевое слово this
Ключевое слово this
может использоваться различными способами и один из этих способов мы рассмотрели выше — для ссылки на текущий экземпляр. При этом, мы можем воспользоваться этим ключевым словом и при создании объектов класса. Например,
internal class Building(double width, double length, double height) { double width = width; double length = length; double height = height; public double GetVolume() => width * length * height; public Building() : this(10, 20, 30) { } }
Обратите внимание на конструктор без параметров. Здесь мы, используя ключевое слово this
указываем, что при вызове этого конструктора необходимо вызвать конструктор с тремя параметрами (сейчас это основной конструктор класса). Теперь создание объекта может выглядеть так:
Building building = new Building();
и, при этом, поля класса получать начальные значения 10, 20 и 30. Таким образом мы можем при создании объектов вызывать не один, а сразу цепочки конструкторов, например,
internal class Building { public double width; public double length; public double height; public double GetVolume() => width * length * height; public Building(): this(10) { } public Building(double width): this(width, 20) { } public Building(double width, double length): this(width, length, 30) { } public Building(double width, double length, double height) { this.width = width; this.length = length; this.height = height; } }
Здесь первый конструктор вызывает второй, а второй — третий.
Инициализаторы объектов
Инициализаторы объектов позволяют присваивать значения всем доступным извне полям и свойствам объекта в момент его создания без явного вызова конструктора. Чтобы продемонстрировать использование инициализаторов объектов, изменим наш класс следующим образом — все поля класса сделаем доступными извне — добавим им модификатор public
class Building { public double width; public double length; public double height; public Building(): this(20, 20, 3) { }
Теперь посмотрим, как мы можем воспользоваться инициализатором объекта:
using FirstClass; Building building = new Building { width = 5, height = 5, length = 5}; Console.WriteLine($"Объем здания: {building.GetVolume()}");
Здесь мы в фигурных скобках указали значение всем публичным полям класса и без явного вызова конструктора создали объект. При этом следует учитывать следующие особенности инициализатора объектов:
- используя инициализатор мы можем установить значения только доступных из внешнего кода полей и свойств объекта. Именно поэтому мы добавили к нашим полям модификатор
public
. - несмотря на то, что явного вызова конструктора не было, конструктор вызывается (в нашем примере был вызван конструктор без параметров) и только после вызова конструктора выполняется инициализатор. Таким образом, мы перезаписали поля нашего объекта в инициализаторе.
Итого
Итак, сегодня мы познакомились с основными моментами создания классов и объектов в C#, создали свой первый класс и научились использовать конструкторы и инициализаторы объектов. Однако, изучение классов и объектов C# на этом не заканчивается. В следующий раз поговорим о свойствах и модификаторах доступа.