Как работать с объектами: 2‑я часть гайда по ООП
2026-02-21 20:08 Diff

#статьи

  • 4 сен 2019
  • 0

Только работа с объектами начинает казаться простой, как тут же появляются новые сюрпризы. Рассказываем, как их избежать.

 vlada_maestro / shutterstock

Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.

Данные, с которыми работает программа, хранятся в оперативной памяти. Для этого используются ячейки, у которых есть адрес (ключ) и значение.

Раньше программистам приходилось запоминать эти ключи или записывать их в отдельный блокнот. Но современные языки программирования позволяют создавать переменные (их называют примитивными или значимыми типами данных), у которых вместо адреса идентификатор:

int sum = 50; //sum — ключ, 50 — значение string name = "John";

Для каждой переменной выделяется одна ячейка памяти. Однако объекты гораздо больше обычной переменной, потому что в них может быть сразу несколько полей. Можно создать переменную hero, в которой будет объект класса Character:

Character hero = new Character();

Но в ней будет храниться не сам объект, а ссылка на него. Поэтому объекты относятся к ссылочным типам данных, и из-за этого работа с ними отличается от работы с примитивными типами.

Рассмотрим такой код:

Character hero = new Character("Player", 50, 20); Character hero1 = new Character("Player", 50, 20); if (hero == hero1) { Console.WriteLine("true"); } else { Console.WriteLine("false"); } Console.ReadKey();

Здесь сначала создаются, а потом сравниваются два идентичных объекта. Если они равны, то выводится true, а иначе — false. Вот что выведет программа:

Несмотря на то что все поля совпадают, программа выводит false. И это не баг. Дело в том, что сравниваются не значения полей объектов, а ссылки на них. А поскольку это два различных объекта, то и адреса у них разные.

Строка true будет выведена только в том случае, если в обеих переменных находится ссылка на один и тот же объект. Чтобы это проверить, в коде из примера выше нужно изменить значение переменной hero1:

Теперь программа выведет true, потому что это один и тот же объект:

И теперь, если изменить hero1, hero тоже изменится:

Character hero = new Character("Player", 50, 20); Character hero1 = hero; hero1.Move("up"); Console.WriteLine(hero.Coordinates); Console.ReadKey();

Тут меняются координаты объекта hero1 с помощью метода Move (), а затем выводятся координаты объекта hero. Они были [50, 20], но теперь по оси Y значение будет 21:

Если нужно проверить, совпадают ли значения полей у объектов, то надо вручную проверять каждое поле:

if (hero.X == hero1.X && hero.Y == hero1.Y) { Console.WriteLine("true"); } else { Console.WriteLine("false"); }

Однако это неудобно, если полей очень много. Чтобы упростить эту задачу, можно использовать метод Equals () — его мы рассмотрим в статье про полиморфизм. Если же надо сделать копию, то можно написать метод Clone () или Copy (). Он будет принимать объект, чтобы скопировать его данные:

public void Clone(Character obj) { this.name = obj.name; this.x = obj.x; this.y = obj.y; this.health = obj.health; }

Или же можно написать конструктор, который будет принимать в качестве аргумента другой объект.

Сразу же стоит отметить, что у передачи объекта в качестве аргумента есть побочные эффекты. Рассмотрим такой код:

static void Main(string[] args) { int num = 2; Multiply(num); Console.WriteLine(num); Console.ReadKey(); } private static void Multiply(int num) { num *= 2; }

В методе Multiply () полученное число умножится на два. Но так как int — это значимый тип, то в метод будет передана не ссылка на это значение в памяти, а само число. То есть будет создана новая переменная (их называют локальными) с таким же значением, и уже она будет умножена на два. На переданное значение это никак не повлияет.

Вот что выведет программа:

Однако если использовать объект, то будет передана ссылка на него, а все операции будут проводиться с ним, а не с его значением:

static void Main(string[] args) { Character hero = new Character(50, 20); Change(hero); Console.WriteLine(hero.Health); Console.ReadKey(); } private static void Change(Character hero) { hero.Health = 4; }

Вот что выведет программа:

Такие особенности ссылочных типов нужно учитывать при разработке программ, чтобы не происходило ничего неожиданного. Например, когда значение меняется, хотя и не должно.

Создайте класс Game, в котором будут храниться все игровые объекты. Дайте персонажу возможность перемещаться по карте и взаимодействовать с объектами, координаты которых совпадают с его позицией.

Подсказка

Используйте коллекции и циклы.

Практикуйтесь и работайте с объектами как можно чаще, и тогда вы сможете овладеть ими. Однако, чтобы писать код без костылей, нужно понимать, как он работает, — в том числе это касается и ссылочных, и значимых типов данных. Кроме того, понимание различий между этими типами избавит вас от неприятных сюрпризов.

Бесплатный курс по Python ➞
Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу