Классы и объекты

Содержание

Введение в Scala.

С нуля до распределенных приложений.

Классы и объекты

15 June 2015 от Tagir

Scala гибридный функциональный/ОО язык и обладает одним из самых мощных аппаратов по работе с классами и типами. Создатель языка, Мартин Одерски — автор generics в языке Java.

Синтаксис создания классов:

Тело класса является основным конструктором. Все поля и функции объявленные в нем являются членами объектов класса.

Дополнительные конструкторы объявляются функциями с названием this и должны вызывать основной конструктор:

Модификаторы доступа

В Scala доступны обычные модификаторы доступа для полей класса:

  • public, доступно всем клиентам класса
  • protected, доступно в классе и всем производным
  • private, доступно только объектам данного класса

Кроме того, модификаторы могут быть расширены квалификаторами [this] или [X]. Первый ограничивает доступ до одного объекта, второй делает поле видимым всем включающим классам или пакетам до X.

Модификаторы по умолчанию

Все поля классов по умолчанию имеют модификатор public.

Сделано это тех соображений, что Scala ориентирована на использование неизменяемых значений, поэтому “утечка” поля класса в интерфейс не дает возможности сломать программу, а только облегчает тестирование.

Параметры конструкторов имеют по умолчанию модификатор private[this]. Для превращения их в поле класса надо добавить в объявление ключевое слово val/var.

x — имеет модификатор public, y — имеет модификатор private[this]

Особый синтаксис

Несколько операций в Scala вызывают методы с определенным названием в классе. Оператор == вызванный на объекте класса вызывает метод equals, а ( ) метод apply.

Объекты-синглтоны

В Scala нет статических (static) методов, но есть особый синтаксис для создания объектов-синглтонов.

Этот объект будет создан в единственном экземпляре и доступен в любое время. Их можно использовать как объекты некоторого класса, для этого достаточно указать, что они унаследованы от него ключевым словом extends.

Объекты-компаньоны

Для каждого класса можно объявить специальный объект-компаньон (companion object). Это объект объявленный в том же файле и с тем же именем, что и класс. Этот объект имеет доступ к private полям класса.

Возможно вы уже встречались с созданием некоторых объектов без использования ключевого слова new. Здесь нет магии, это вызов метода apply на объекте-компаньоне.

Часто дополнительные конструкторы класса объявляют именно так.

5. Java — Классы и объекты

Java является объектно-ориентированным языком программирования. Как язык, который имеет функцию объектно-ориентирования, он поддерживает следующие основные понятия:

  • полиморфизм;
  • наследование;
  • инкапсуляция;
  • абстракция;
  • классы;
  • объекты;
  • экземпляр;
  • метод;
  • парсинг.

В этом уроке мы рассмотрим объекты и классы в Java, их концепции.

Класс может быть определен как шаблон (обозначен зеленым цветом), который описывает поведение объекта, который в свою очередь имеет состояние и поведение. Он является экземпляром класса. Например: собака может иметь состояние — цвет, имя, а также и поведение — кивать, лаять, есть.

Содержание

Объекты в Java

Давайте теперь посмотрим вглубь, что является объектами. Если мы рассмотрим реальный мир, то найдём много предметов вокруг нас, автомобили, собаки, люди, и т.д. Все они имеют состояние и образ жизни.

Если учесть, собаку, то ее состояние — имя, порода, цвет, а образ жизни — лай, виляние хвостом, бег.

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

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

Классы в Java

Класс, из которого создаются отдельные объекты, обозначен зеленым цветом.

Пример создания класса в Java, приводится ниже:

Класс может содержать любой из следующих видов переменных:

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

В Java классы могут иметь любое количество методов для доступа к значению различных видов методов. В приведенном выше примере, barking(), hungry() и sleeping() являются методами.

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

Конструктор класса

При обсуждении вопроса класса, одной из наиболее важных подтем в Java является конструктор. Каждый класс имеет конструктор. Если мы не напишем его или, например, забудем, компилятор создаст его по умолчанию для этого класса.

Каждый раз, когда в Java создается новый объект, будет вызываться по меньшей мере один конструктор. Главное правило является то, что они должны иметь то же имя, что и класс, который может иметь более одного конструктора.

Пример конструктора приведен ниже:

Примечание: в следующих разделах мы будем более подробно обсуждать, если у нас будет два разных типа конструкторов.

Создание объекта

Варианты как создать объект в классе следующие:

  • Объявление: объявление переменной с именем переменной с типом объекта.
  • Инстанцирование: используется «новое» ключевое слово.
  • Инициализация: «новое» ключевое слово сопровождается вызовом конструктора. Этот вызов инициализирует новый объект.

Пример приводится ниже:

Если Вы скомпилируете и запустите выше программу, то она выдаст следующий результат:

Доступ к переменным экземпляра и методам в Java

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

Пример

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

Если Вы скомпилируете и запустите выше программу, то она выдаст следующий результат:

Правила объявления классов, операторов импорта и пакетов в исходном файле

В последней части этого раздела давайте рассмотрим правила декларации исходного файла. Эти правила в Java имеют важное значение при объявлении классов, операторов импорта и операторов пакета в исходном файле.

  • В исходном файле может быть только один публичный класс (public class).
  • Исходный файл может иметь несколько «непубличных» классов.
  • Название публичного класса должно совпадать с именем исходного файла, который должен иметь расширение .java в конце. Например: имя класса public class Employee<>, то исходный файл должен быть Employee.java.
  • Если класс определен внутри пакета, то оператор пакет должно быть первым оператором в исходном файле.
  • Если присутствуют операторы импорта, то они должны быть написаны между операторами пакета и объявлением класса. Если нет никаких операторов пакета, то оператор импорта должен быть первой строкой в исходном файле.
  • Операторы импорта и пакета будут одинаково выполняться для всех классов, присутствующих в исходном файле. В Java не представляется возможным объявить различные операторы импорта и/или пакета к различным классам в исходном файле.

Классы имеют несколько уровней доступа и существуют различные типы классов: абстрактные классы (abstract class), конечные классы (final class) и т.д. Обо всем этом обсудим в уроке модификаторы доступа.

Помимо указанных выше типов классов, Java также имеет некоторые специальные классы, называемые внутренние (Inner class) и анонимные классы (Anonymous class).

Java пакет (package)

При разработке приложений сотни классов и интерфейсов будет написано, поэтому категоризации этих классов является обязательным, а также это делает жизнь намного проще.

Операторы импорта (import)

Если задать полное имя, которое включает в себя пакет и имя класса, то компилятор может легко найти исходный код или классы. В Java импорт это способ задать правильное место для компилятора, чтобы найти конкретный класс.

Например, следующая строка будет просить компилятор загрузить все классы, доступные в каталоге «java_installation/java/io»:

Простой пример по выше описанному

Для нашего обучения создадим два класса. Это будут классы Employee и EmployeeTest.

Для начала откройте блокнот и добавьте следующий код. Помните, что этот класс Employee является открытым или публичным классом. Теперь сохраните исходный файл с именем Employee.java.

Класс Employee имеет четыре переменных экземпляра name, age, designation и salary. Он имеет один явно определенный конструктор, который принимает параметр.

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

Ниже приводится класс EmployeeTest, в котором создаются два экземпляра класса Employee и вызывают методы для каждого объекта, для присвоения значений каждой переменной.

Сохраните следующий код в файл «EmployeeTest.java»:

Теперь, скомпилировав оба класса, запустим EmployeeTest и получим следующий результат:

В следующем уроке обсудим основные типы данных, и как они могут быть использованы при разработке java-приложений.

Классы и объекты

Код выглядит менее уродливо, но с переменными surname, surname2 явно что-то не так. Особенно если людей станет больше. Неплохо бы собрать все данные, относящиеся к одному человеку, в кучку. И сразу добавить к ним те функции, которые не нужны другие данные.

По сути дела нам надо перейти от типа данных «строка» к новому типу данных «человек», в который на данный момент будут входить три строки — фамилия имя и отчество. Само собой для этого давным-давно придуманы специальные инструменты.

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

А вот каждый конкретный человек со своими значениями ФИО будет экземпляром класса человек. Точно так же как каждый ящик со своими значениями высоты, ширины и глубины будет являться экземпляром класса ящик. Само собой у каждого класса может быть масса других параметров — например и у человека и у ящика может быть параметр масса, у человека может быть параметр пол — мужской или женский, а вот у ящика пол вряд ли будет (хотя кто знает, что нам готовит непредсказуемое будущее)

Еще раз приведу весь текст программы (файл Program.cs), чтоб вы обратили внимание где именно находится описание класса.

static void Main(string[] args)

// создаем два экземпляра класса человек с разными фио

Person person1 = new Person(«Пушкин», «Александр», «Сергеевич»);

Person person2 = new Person(«Гончарова», «Наталья», «Николаевна»);

// выводим разные фио используя встроенные в класс функции — методы

//статическая функция одинакова для всех экземпляров класса и вызывается с имени самого класса

public class Person

// три строковых переменные-свойства, доступные извне класса — public

public string Name = «»;

public string Surname = «»;

public string Otchestvo = «»;

// конструктор, специальная функция, которая вызывается при создани экземпляра класса с помощью слова new

public Person(string surname, string name, string otchestvo)

// этим функциям не надо подавать на вход данные — они используют данные экземпляра класса

public string GetFio()

string fio = Surname + » » + Name + » » + Otchestvo;

public string GetFioInitials()

string fio = Surname + » » + Name.Substring(0, 1) + «. » + Otchestvo.Substring(0, 1) + «.»;

// статическая функция не используют данные экземпляра, она одинакова для всех экземпляров. По сути дела класс для нее — просто название группы функций

public static string GetClassDescription()

return «Класс Person. Хранит данные о человеке.»;

В профессиональном программировании считается дурным тоном давать свободный и бесконтрольный доступ к хранимым в классе данным. Чтобы запретить доступ извне к свойству Surname для него надо прописать другой модификатор доступа, вместо public — private. Записывать и считывать данные надо будет через специальные функции, аналогичные тем функциям, которые на самом деле что-то вычисляют в духе GetFio. Чтобы не возиться с написанием кучи функций типа GetName и SetName в C# применяется специальный механизм свойств, на основании которых уже при компиляции программы автоматически генерятся эти однотипные функции. Такой способ облегчить жизнь программисту называется синтаксическим сахаром. В Visual Studio есть даже специальная функция для этого.

В этом же меню есть много других крайне полезных пунктов — например переименование переменной, класса, пространства имен или функции сразу во всех местах проекта.

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Студент — человек, постоянно откладывающий неизбежность. 10530 — | 7319 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Тип данных “класс”. Объекты и классы.

Тип данных “класс”. Объекты и классы.

Класс — это такая абстракция множества предметов реального мира, что

1. Предметы в этом множестве — объекты имеют одни и те же характеристики

2. Все объекты подчинены и согласованы с одним и тем же набором правил и линий поведений

Классы и объекты, понятие экземпляра класса, понятие членов класса

В объектно-ориентированной программе с применением классов каждый объект является «экземпляром» некоторого конкретного класса, и других объектов не предусмотрено. То есть «экземпляр класса» в данном случае означает не «пример некоторого класса» или «отдельно взятый класс», а «объект, типом которого является какой-то класс». При этом в разных языках программирования допускается либо не допускается существование еще каких-то типов данных, экземпляры которых не являются объектами (то есть язык определяет, являются ли объектами такие вещи, как числа, массивы и указатели, или не являются, и, соответственно, есть ли такие классы как «число», «массив» или «указатель», экземплярами которых были бы каждое конкретное число, массив или указатель).

Например, абстрактный тип данных «строка текста» может быть оформлен в виде класса, и тогда все строки текста в программе будут являться объектами — экземплярами класса «строка текста».

При использовании классов все элементы кода программы, такие как переменные, константы, методы, процедуры и функции, могут принадлежать (а во многих языках обязаны принадлежать) тому или иному классу. Сам класс в итоге определяется как список своих членов, а именно полей (свойств) и методов/функций/процедур. В зависимости от языка программирования к этому списку могут добавиться константы, атрибуты и внешние определения.

Как и структуры, классы могут задавать поля — то есть переменные, принадлежащие либо непосредственно самому классу (статические), либо экземплярам класса (обычные). Статические поля существуют в одном экземпляре на всю программу (или, в более сложном варианте, — в одном экземпляре на процесс или на поток/нить). Обычные поля создаются по одной копии для каждого конкретного объекта — экземпляра класса. Например, общее количество строк текста, созданных в программе за время её работы, будет являться статическим полем класса «строка текста». А конкретный массив символов строки будет являться обычным полем экземпляра класса «строка текста», так же как переменная «фамилия», имеющая тип «строка текста», будет являться обычным полем каждого конкретного экземпляра класса «человек».

В ООП при использовании классов весь исполняемый код программы (алгоритмы) будет оформляться в виде так называемых «методов», «функций» или «процедур», что соответствует обычному структурному программированию, однако теперь они могут (а во многих языках обязаны) принадлежать тому или иному классу. Например, по возможности, класс «строка текста» будет содержать все основные методы/функции/процедуры, предназначенные для работы со строкой текста, такие как поиск в строке, вырезание части строки и т. д.

Как и поля, код в виде методов/функций/процедур, принадлежащих классу, может быть отнесен либо к самому классу, либо к экземплярам класса. Метод, принадлежащий классу и соотнесенный с классом (статический метод) может быть вызван сам по себе и имеет доступ к статическим переменным класса. Метод, соотнесенный с экземпляром класса (обычный метод), может быть вызван только у самого объекта, и имеет доступ как к статическим полям класса, так и к обычным полям конкретного объекта (при вызове этот объект передастся скрытым параметром метода). Например, общее количество созданных строк можно узнать из любого места программы, но длину конкретной строки можно узнать только указав, тем или иным образом, длину какой строки будем мерить.

Сам класс не является объектом, однако, в зависимости от языка программирования и платформы, программисту могут быть доступны те или иные объекты, позволяющие получить информацию о классе — название, список членов, объем памяти, занимаемой классом или отдельным экземпляром. Например, может существовать специальный класс «тип данных», экземпляры которого описывают тот или иной конкретный класс, существующий в программе.

Виды классов

§ Базовый (родительский) класс

§ Производный класс (наследник, потомок)

Классы в языке Object Pascal (среда Delphi)

На языке Delphi класс описывается следующим образом:

TMy >§ TMyClass — имя класса;

§ class — ключевое слово, начинающее определение класса;

§ TObject — класс-предок, если есть наследование;

§ private, protected, public, published — ключевые слова, обозначающие секции областей доступа.

Методы класса

Метод представляет собой подпрограмму (процедуру или функцию), предназначенную для обработки полей.

Описание методов похоже на описание обычной подпрограммы модуля. Заголовок метода располагается в описании класса, а сам код метода находится в разделе реализации. Имя метода в разделе реализации является составным и включает в себя тип класса.

Метод, объявленный в классе, может вызываться различными способами, что зависит от вида этого метода. Вид метода определяется модификатором, который указывается в описании класса после заголовка метода и отделяется от заголовка точкой с запятой: virtual (виртуальный метод); dynamic (динамический метод) и др.

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

Процедуры и функции, предназначенные для выполнения над объектами действий, называются методами. Предварительное объявление методов выполняется при описании класса в секции interface модуля, а их программный код записывается в секции implementation. Однако в отличие от обычных процедур и функций заголовки методов должны содержать наименование класса.

· Статические – все методы по умолчанию. При переопределении метода в классе – наследнике то отменятся родительский метод для всех объектов этого класса.

· Виртуальный и динамический методы не имеют ничего общего с методами с такими же именами в классах – наследниках. Очень часто применяется полиморфизм – когда создается виртуальный метод для объектов базового класса. При объявлении таких методов добавляются ключевые слова dynamic или virtual, которые завершают объявление метода. Для перегрузки метода ставим слово override.

· Если виртуальный или динамический метод не определен в объявленном классе, то такой метод называют Абстрактный. Такой метод будет перегружен в классах – наследниках. Из этого следует, что в том классе, где он перегружен, можно его вызывать. Ключевое слово – abstract. Перегрузка метода – overload, а для виртуального метода – добавляется слово reintroduce.

Свойства класса и поля. Отличие свойства класса от полей записи.

Поля класса представляют собой данные, содержащиеся в классе, т.е. поля служат для хранения информации об объекте. Поле описывается как обычная переменная и может принадлежать любому типу.

Согласно принятому обозначению в Delphi имена полей должно начинаться с буквы F (Field – поле), а имена классов с буквы T.

Изменение значений полей обычно выполняется с помощью методов и свойств объекта.

Свойства реализуют механизм доступа к полям. Каждому свойству соответствует поле, содержащее значение свойства, и два метода, обеспечивающих доступ к этому полю.

Описание свойства начинается со слова property, при этом типы свойства и соответствующего поля должны совпадать.

Назначение конструктора

Одна из ключевых особенностей ООП — инкапсуляция: внутренние поля объекта напрямую недоступны, и пользователь может работать с объектом только как с единым целым, через открытые (public) методы. Каждый метод, в идеале, должен быть устроен так, чтобы объект, находящийся в «допустимом» состоянии (то есть когда выполняется инвариант класса), после вызова метода также оказался в допустимом состоянии. И первая задача конструктора — перевести поля объекта в такое состояние.

Вторая задача — упростить пользование объектом. Объект — не «вещь в себе», ему часто приходится требовать какую-то информацию от других объектов: например, объектFile, создаваясь, должен получить имя файла. Это можно сделать и через метод:

Виды конструкторов

Некоторые языки программирования различают несколько особых типов конструкторов:

§ конструктор по умолчанию — конструктор, не принимающий аргументов;

§ конструктор копирования — конструктор, принимающий в качестве аргумента объект того же класса (или ссылку из него);

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

Конструктор по умолчанию

Основная статья: Конструктор по умолчанию

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

Конструктор копирования

Основная статья: Конструктор копирования

Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.

Конструктор копирования в основном необходим, когда объект имеет указатели на объекты выделенные в куче. Если программист не создаёт конструктор копирования, то компилятор создаст неявный конструктор копирования, который копирует указатели как есть, то есть фактическое копирование данных не происходит и два объекта ссылаются на одни и те же данные в куче. Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.

Аргумент должен передаваться именно по ссылке, а не по значению. Это вытекает из коллизии: при передаче объекта по значению (в частности, для вызова конструктора) требуется скопировать объект. Но для того, чтобы скопировать объект, необходимо вызвать конструктор копирования.

Конструктор преобразования

Конструктор, принимающий один аргумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.

Виртуальный конструктор

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

«Виртуальными конструкторами» называют похожий, но другой механизм, присутствующий в некоторых языках — например, он есть в Delphi, но нет в C++ и Java. Этот механизм позволяет создать объект любого заранее неизвестного класса при двух условиях:

§ этот класс является потомком некоего наперёд заданного класса (в данном примере это класс TVehicle);

§ на всём пути наследования от базового класса к создаваемому цепочка переопределения не обрывалась. При переопределении виртуального метода синтаксис Delphi требует ключевое слово overload, чтобы старая и новая функции с разными сигнатурами могли сосуществовать, override для переопределения функции либо reintroduce для задания новой функции с тем же именем — последнее недопустимо.

Delphi

В Delphi, в отличие от C++, для объявления конструктора служит ключевое слово constructor. Имя конструктора может быть любым, но рекомендуется называть конструктор Create.

Пример

T >Деструктор — специальный метод класса, служащий для деинициализации объекта (например освобождения памяти).

Деструктор в Delphi

Для объявления деструктора в Delphi используется ключевое слово destructor. Имя деструктора может быть любым, но рекомендуется всегда называть деструктор Destroy.

T >В Delphi все классы являются потомками, по крайней мере, класса TObject, поэтому, для корректного освобождения памяти, необходимо перекрывать деструктор, используя директиву override.

В Delphi прямой вызов деструктора используется редко. Вместо него используют метод Free.

Метод Free вначале проверяет существует ли уничтожаемый объект, а затем вызывает деструктор. Этот прием позволяет избегать ошибок, возникающих при обращении к несуществующему объекту.

Простое наследование

Класс, от которого произошло наследование, называется базовым или родительским (англ. base class). Классы, которые произошли от базового, называются потомками,наследниками или производными классами (англ. derived class).

В некоторых языках используются абстрактные классы. Абстрактный класс — это класс, содержащий хотя бы один абстрактный метод, он описан в программе, имеет поля,методы и не может использоваться для непосредственного создания объекта. То есть от абстрактного класса можно только наследовать. Объекты создаются только на основе производных классов, наследованных от абстрактного. Например, абстрактным классом может быть базовый класс «сотрудник вуза», от которого наследуются классы «аспирант», «профессор» и т. д. Так как производные классы имеют общие поля и функции (например, поле «год рождения»), то эти члены класса могут быть описаны в базовом классе. В программе создаются объекты на основе классов «аспирант», «профессор», но нет смысла создавать объект на основе класса «сотрудник вуза».

Множественное наследование

Основная статья: Множественное наследование

При множественном наследовании у класса может быть более одного предка. В этом случае класс наследует методы всех предков. Достоинства такого подхода в большей гибкости. Множественное наследование реализовано в C++. Из других языков, предоставляющих эту возможность, можно отметить Python и Эйфель. Множественное наследование поддерживается в языке UML.

Множественное наследование — потенциальный источник ошибок, которые могут возникнуть из-за наличия одинаковых имен методов в предках. В языках, которые позиционируются как наследники C++ (Java, C# и др.), от множественного наследования было решено отказаться в пользу интерфейсов. Практически всегда можно обойтись без использования данного механизма. Однако, если такая необходимость все-таки возникла, то, для разрешения конфликтов использования наследованных методов с одинаковыми именами, возможно, например, применить операцию расширения видимости — «::» — для вызова конкретного метода конкретного родителя.

Попытка решения проблемы наличия одинаковых имен методов в предках была предпринята в языке Эйфель, в котором при описании нового класса необходимо явно указывать импортируемые члены каждого из наследуемых классов и их именование в дочернем классе.

Большинство современных объектно-ориентированных языков программирования (C#, Java, Delphi и др.) поддерживают возможность одновременно наследоваться от класса-предка и реализовать методы нескольких интерфейсов одним и тем же классом. Этот механизм позволяет во многом заменить множественное наследование — методы интерфейсов необходимо переопределять явно, что исключает ошибки при наследовании функциональности одинаковых методов различных классов-предков.

Delphi (Object Pascal)

Для использования механизма наследования в Delphi необходимо в объявлении класса справа от слова class указать класс предок:

TAncestor = classprivateprotectedpublic // Виртуальная процедура procedure VirtualProcedure; virtual; abstract; procedure StaticProcedure;end;

TDescendant = class(TAncestor)privateprotectedpublic // Перекрытие виртуальной процедуры procedure VirtualProcedure; override; procedure StaticProcedure;end;

Абсолютно все классы в Delphi являются потомками класса TObject. Если класс-предок не указан, то подразумевается, что новый класс является прямым потомком классаTObject.

Множественное наследование в Delphi частично поддерживается за счёт использования классов-помощников (Сlass Helpers).

Определение инкапсуляции.

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

§ Пользователь может взаимодействовать с объектом только через этот интерфейс. Реализуется с помощью ключевого слова: public.

§ Пользователь не может использовать закрытые данные и методы. Реализуется с помощью ключевых слов: private, protected, internal.

Инкапсуляция — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией, полиморфизмом и наследованием).

Сокрытие реализации целесообразно применять в следующих случаях:

§ предельная локализация изменений при необходимости таких изменений,

§ прогнозируемость изменений (какие изменения в коде надо сделать для заданного изменения функциональности) и прогнозируемость последствий изменений.

Delphi

В Delphi для создания скрытых полей или методов их достаточно объявить в секции private.

TMy >Для создания интерфейса доступа к скрытым полям в Delphi введены свойства.

Определение полиморфизма.

Полиморфи́зм (в языках программирования) — возможность объектов с одинаковой спецификацией иметь различную реализацию.

Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию — например, реализация класса может быть изменена в процессе наследования [1] .

Кратко смысл полиморфизма можно выразить фразой: «Один интерфейс, множество реализаций».

Полиморфизм — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией, инкапсуляцией и наследованием).

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

§ внешняя общность проявляется как одинаковый набор методов с одинаковыми именами и сигнатурами (именем методов и типами аргументов и их количеством);

§ внутренняя общность — одинаковая функциональность методов. Её можно описать интуитивно или выразить в виде строгих законов, правил, которым должны подчиняться методы. Возможность приписывать разную функциональность одному методу (функции, операции) называется перегрузкой метода (перегрузкой функций, перегрузкой операций).

Примеры

Класс геометрических фигур (эллипс, многоугольник) может иметь методы для геометрических трансформаций (смещение, поворот, масштабирование).

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

Классы

Для поддержки ООП в язык Delphi введены объектные типы данных, с помощью которых одновременно описываются данные и операции над ними. Объектные типы данных называют классами, а их экземпляры — объектами.

Классы объектов определяются в секции type глобального блока. Описание класса начинается с ключевого слова class и заканчивается ключевым словом end. По форме объявления классы похожи на обычные записи, но помимо полей данных могут содержать объявления пользовательских процедур и функций. Такие процедуры и функции обобщенно называют методами, они предназначены для выполнения над объектами различных операций. Приведем пример объявления класса, который предназначен для чтения текстового файла в формате «delimited text» (файл в таком формате представляет собой последовательность строк; каждая строка состоит из значений, которые отделены друг от друга символом-разделителем):

type TDelimitedReader = class // Поля FileVar: TextFile; Items: array of string; Delimiter: Char; // Методы procedure PutItem(Index: Integer; const Item: string); procedure SetActive(const AActive: Boolean); function ParseLine(const Line: string): Integer; function NextLine: Boolean; function GetEndOfFile: Boolean; end;

Класс содержит поля (FileVar, Items, Delimiter) и методы (PutItem, SetActive, ParseLine, NextLine, GetEndOfFile). Заголовки методов, (всегда) следующие за списком полей, играют роль упреждающих (forward) описаний. Программный код методов пишется отдельно от определения класса и будет приведен позже.

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

Класс содержит несколько полей:

  • FileVar — файловая переменная, необходимая для доступа к файлу;
  • Delimiter — символ, который служит разделителем элементов;
  • Items — массив элементов, полученных разбором последней считанной строки;

Класс также содержит ряд методов (процедур и функций):

  • PutItem — помещает элемент в массив Items по индексу Index; если индекс превышает верхнюю границу массива, то размер массива автоматически увеличивается;
  • SetActive — открывает или закрывает файл, из которого производится чтение строк;
  • ParseLine — осуществляет разбор строки: выделяет элементы из строки и помещает их в массив Items; возвращает количество выделенных элементов;
  • NextLine — считывает очередную строку из файла и с помощью метода ParseLine осуществляет ее разбор; в случае успешного чтения очередной строки функция возвращает значение True, а иначе — значение False (достигнут конец файла);
  • GetEndOfFile — возвращает булевское значение, показывающее, достигнут ли конец файла.

Обратите внимание, что приведенное выше описание является ничем иным, как декларацией интерфейса для работы с объектами класса TDelimitedReader. Реализация методов PutItem, SetActive, ParseLine, NextLine и GetEndOfFile на данный момент отсутствует, однако для создания и использования экземпляров класса она пока и не нужна.

В некотором смысле объекты похожи на программные модули, для использования которых необходимо изучить лишь интерфейсную часть, раздел реализации для этого изучать не требуется. Поэтому дальше от описания класса мы перейдем не к реализации методов, а к созданию на их основе объектов.

StringGrid.

Компонент StringGrid находится на странице Additionalпалитры компонентов. Там находятся «дополнительные» компоненты, но StringGrid Delphi, на мой взгляд, достоин большего уважения, лично я разместил бы его на странице Standart! StringGrid — компонент для отображения различных данных в табличной форме. Как следует из названия, ячейки компонента StringGrid Delphi могут содержать данные, имеющие тип String, а также отображать графику.

Таблица StringGrid состоит из выделенных серым FixedCols и FixedRows — зафиксированных ячеек-заголовков, и обычных, белых ячеек. Содержимое Fixed ячеек недоступно редактированию, и меняется только программно. За возможность редактирования обычных ячеек отвечает одно из значений свойства Options.

Итак, компонент StringGrid имеет возможность адресации каждой отдельной ячейки по номеру столбца и строки. Содержимое ячейки (i, j), где где i — номер столбца, j — номер строки, имеет вид

StringGrid1.Cells[i, j]

и доступно как для чтения, так и для записи. Здесь, как и всегда, номера столбцов ( i ) и строк ( j ) отсчитываются от 0.

Выделенная ячейка таблицы имеет

номер столбца: StringGrid1.Col
номер строки: StringGrid1.Row

поэтому содержимое выделенной ячейки будет адресоваться так:

Не правда ли, написание такой строки — утомительный процесс. Поэтому пользуйтесь оператором присоединения with:

with StringGrid1 do
S:=Cells[Col, Row];

А лучше сразу задать в свойстве Name имя покороче, например SG.

За многие свойства компонента Delphi StringGrid отвечает свойство Options. В Инспекторе Объектов Options — это раскрывающийся список, представляющий собой элементы данногомножества. Если значение элемента равно True, то он присутствует в множестве, если False — то нет.

Свойство Значение
goFixedVertLine Наличие вертикальных разделительных линий между «фиксированными» ячейками
goFixedHorzLine Наличие горизонтальных разделительных линий между «фиксированными» ячейками
goVertLine Наличие вертикальных разделительных линий между «обычными» ячейками
goHorzLine Наличие горизонтальных разделительных линий между «обычными» ячейками
goRangeSelect Возможность выделить диапазон ячеек
goDrawFocusSelected Закрашивание ячейки с фокусом ввода
goRowSizing Возможность менять высоту строк мышкой
goColSizing Возможность менять ширину столбцов мышкой
goRowMoving Возможность менять номер строки, то есть перемещать её, мышкой
goColMoving Возможность менять номер столбца, то есть перемещать его, мышкой
goEditing Возможность редактировать содержимое ячейки с клавиатуры
goTabs При значении True фокус смещается на следующую ячейку в таблице,False — на следующий компонент
goRowSelect Выделяется вся строка с «фокусированной» ячейкой
goAlwaysShowEditor При значении True содержимое ячейки при получении фокуса сразу доступно редактированию, False — сначала необходимо щёлкнуть по ней мышкой, либо нажать Enter или F2 (прим.: не действует приgoRowSelect=True)
goThumbTracking При значении True перемещение «бегунка» прокрутки мышкой вызывает немедленное перемещение ячеек,False — ячейки перемещаются только при отпускании «бегунка»

Как следует из таблицы, за возможность редактировать содержимое ячеек с клавиатуры отвечает элемент goEditing свойства-множества Options. В Инспекторе Объектов установите его значение вTrue. Чтобы управлять этой возможностью программно, нужно включить или исключить из множества данный элемент:

Если элементы заданы списком, это аналогично присвоению в Инспекторе Объектов этим элементам значения True, остальным — False.

Ячеек в таблице, как правило, много, и в рамках компонента видна только часть из них. В программе доступна информация как об общем количестве строк и столбцов, так и номерах и количестве строк и столбцов, видимых в рамках таблицы.

Количество строк в Delphi StringGrid равно StringGrid1.RowCount.
Количество столбцов в Delphi StringGrid равно StringGrid1.ColCount.

Если ячейки не помещаются в таблице, появляются полосы прокрутки. При прокручивании

Илон Маск рекомендует:  Сравнение материнских плат ASUS на чипсете H81
StringGrid1.LeftCol Номер столбца, видимого самым левым
StringGrid1.TopRow Номер строки, видимой самой верхней
StringGrid1.VisibleColCount Количество столбцов, видимых в рамках таблицы
StringGrid1.VisibleRowCount Количество строк, видимых в рамках таблицы

У таблицы StringGrid также есть свойство и для управления размером ячеек.Для всех ячеек

DefaultRowHeight — высота строк по умолчанию
DefaultColWidth — ширина столбцов по умолчанию

Эти значения ширины и высоты принимают все новые ячейки. При необходимости индивидуально установить ширину и высоту столбцов и строк соответственно, пользуемся свойствами

RowHeights[i] — массив, содержащий высоты строк с номером i
ColWidths[i] — массив, содержащий ширины столбцов с номером i

Все эти свойства настраиваем в обработчике события OnCreate Формы, так же как и надписи заголовков, располагающиеся в строках и столбцах «фиксированной» зоны таблицы. В результате таблица появляется уже в «настроенном» виде!

Отдельно требуется осветить вопрос очистки содержимого таблицы StringGrid. Так как таблицаStringGrid, в отличие от, например, компонента Memo, не имеет метода для очистки содержимого сразу всех ячеек, то для удаления внесённых в таблицу ранее данных приходится очищать каждую ячейку отдельно. Делается это двумя вложенными циклами for, пробегающими по столбцам и строкам:

var i, j: Integer;
begin
with StringGRid1 do
for i:=1 to RowCount-1 do //Заголовки строк не трогаем
for j:=1 to ColCount-1 do //Заголовки столбцов не трогаем
Cells[j, i]:=»;
end;

Хотя, оказывается, есть метод для очищения содержимого целого столбца или строки:

StringGrid1.Cols[i].Clear; //Очищается столбец с номером i
StringGrid1.Rows[i].Clear; //Очищается строка с номером i

Очевидно, очищение этими методами гораздо быстрее. Однако будут очищены и ячейки фиксированной зоны, содержащие, например, названия строк и столбцов, которые удалять не нужно. Их после очистки нужно просто «написать» заново, на глаз эта манипуляция совершенно незаметна. Для очистки всей таблицы достаточно последовательно очистить только строки или только столбцы:

var i, j: Integer;
begin
with StringGRid1 do
for i:=1 to RowCount-1 do //Заголовки столбцов не трогаем — цикл от 1
begin
Rows[i].Clear;
Cells[0, i]:=»Заголовок строки i»;
end;
end;

Казалось бы, можно поступить и по-другому, просто обнулить количество строк или столбцов! Однако так делать неправильно, так как при их последующем добавлении может оказаться, что каждая ячейка содержит прежние данные. А в Delphi4 даже при уменьшении количества строк или столбцов содержавшиеся в них данные вообще не пропадали, а так и повисали в воздухе! Так что так можно поступать только если в добавляемых ячейках сразу будет новое непустое содержимое.

Компонент StringGrid умеет не хранить в своих ячейках не только текстовую информацию, но и графику. Графические возможности StringGrid определяются наличием у таблицы свойстваCanvas — холста, на котором можно воспроизводить любую графику стандартными методами Delphi. Кроме того, компонент StringGrid имеет дополнительные методы, помогающие выводу графики в ячейки компонента. Графическими методами в таблице StringGrid можно, например, выводить текст в ячейке не только в одну, но и в несколько строк, произвольно раскрашивать ячейки, размещать рисунки и т.д.

Работа с графическими свойствами компонента StringGrid происходит в обработчике OnDrawCell. По событию OnDrawCell происходит перерисовка таблицы, и следовательно, код в обработчике этого события будет управлять выводом на холст таблицы необходимой графики. Переменные этого обработчика помогут определить и прямоугольник, в котором будет происходить вывод графики:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin
//ACol — индекс столбца
//ARow — индекс строки
//Rect — прямоугольник вывода, заданный ячейкой (ACol, ARow)
end;

Для начала давайте выведем в ячейку (1, 1) компонента StringbGrid какой-нибудь рисунок. Вывести рисунок в ячейку компонента StringGrid проще всего, предварительно загрузив его в компонент Image:

Загрузить рисунок в компонент Image можно, конечно, уже на этапе проектирования, в Инспекторе Объектов, вызвав графический редактор нажатием кнопочки в свойстве Picture.

Затем нужно определить размеры загруженного рисунка:

Далее, готовим ячейку под размещение рисунка. Для этого нужно задать её размеры кратными размерам рисунка. Например, сделаем размеры ячейки в 10 раз меньше размеров рисунка:

Все эти манипуляции делаем предварительно, в обработчике OnCreate Формы, например. Ну и, наконец, в обработчике OnDrawCell выводим рисунок:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin
if ACol*ARow=1 then //Условие ACol*ARow=1 тождественно (ACol=1)and(ARow=1)
StringGrid1.Canvas.StretchDraw(Rect, Image1.Picture.Graphic);
end;

Таким образом, при перерисовке таблица просматривает все ячейки и, встретив комбинацию ACol=1 и ARow=1, выводит в эту ячейку рисунок.

Таким же способом можно и раскрасить заданные по любому условию ячейки в нужные цвета. Например, раскрасим ячейки с положительными числами в зелёный цвет, с отрицательными — в красный, с равными нулю в синий:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var X: Real;
begin
with StringGrid1 do
begin
try
X:=StrToFloat(Cells[ACol, ARow]);
if X>0 then Canvas.Brush.Color:=clGreen;
if X

Последнее изменение этой страницы: 2020-04-08; Нарушение авторского права страницы

Классы и объекты Classes and objects

Классы являются основным типом в языке C#. Classes are the most fundamental of C#’s types. Класс представляет собой структуру данных, которая объединяет в себе значения (поля) и действия (методы и другие функции-члены). A class is a data structure that combines state (fields) and actions (methods and other function members) in a single unit. Класс предоставляет определение для динамически создаваемых экземпляров класса, которые также именуются объектами. A class provides a definition for dynamically created instances of the class, also known as objects. Классы поддерживают механизмы наследования и полиморфизма, которые позволяют создавать производные классы, расширяющие и уточняющие определения базовых классов. Classes support inheritance and polymorphism, mechanisms whereby derived classes can extend and specialize base classes.

Новые классы создаются с помощью объявлений классов. New classes are created using class declarations. Объявление класса начинается с заголовка, в котором указаны атрибуты и модификаторы класса, имя класса, базовый класс (если есть) и интерфейсы, реализуемые этим классом. A class declaration starts with a header that specifies the attributes and modifiers of the class, the name of the class, the base class (if given), and the interfaces implemented by the class. За заголовком между разделителями < и >следует тело класса, в котором последовательно объявляются все члены класса. The header is followed by the class body, which consists of a list of member declarations written between the delimiters < and >.

Вот простой пример объявления класса с именем Point : The following is a declaration of a simple class named Point :

Экземпляры классов создаются с помощью оператора new , который выделяет память для нового экземпляра, вызывает конструктор для инициализации этого экземпляра и возвращает ссылку на экземпляр. Instances of classes are created using the new operator, which allocates memory for a new instance, invokes a constructor to initialize the instance, and returns a reference to the instance. Следующие инструкции создают два объекта Point и сохраняют ссылки на них в две переменные: The following statements create two Point objects and store references to those objects in two variables:

Занимаемая объектом память автоматически освобождается, когда объект становится недоступен. The memory occupied by an object is automatically reclaimed when the object is no longer reachable. В C# нет ни необходимости, ни возможности освобождать память объектов явным образом. It is neither necessary nor possible to explicitly deallocate objects in C#.

Участники Members

Члены класса могут быть статическими членами или членами экземпляра. The members of a class are either static members or instance members. Статические члены принадлежат классу в целом, а члены экземпляра принадлежат конкретным объектам (экземплярам классов). Static members belong to classes, and instance members belong to objects (instances of classes).

Ниже перечислены виды членов, которые могут содержаться в классе. The following provides an overview of the kinds of members a class can contain.

  • Константы Constants
    • Константные значения, связанные с классом. Constant values associated with the class
  • Поля Fields
    • Переменные класса. Variables of the class
  • Методы Methods
    • Вычисления и действия, которые может выполнять класс. Computations and actions that can be performed by the class
  • Свойства Properties
    • Действия, связанные с чтением и записью именованных свойств класса. Actions associated with reading and writing named properties of the class
  • Индексаторы Indexers
    • Действия, реализующие индексирование экземпляров класса, чтобы обращаться к ним как к массиву. Actions associated with indexing instances of the class like an array
  • События Events
    • Уведомления, которые могут быть созданы этим классом. Notifications that can be generated by the class
  • Операторы Operators
    • Поддерживаемые классом операторы преобразования и выражения. Conversions and expression operators supported by the class
  • Конструкторы Constructors
    • Действия, необходимые для инициализации экземпляров класса или класса в целом. Actions required to initialize instances of the class or the class itself
  • Методы завершения Finalizers
    • Действия, выполняемые перед окончательным удалением экземпляров класса. Actions to perform before instances of the class are permanently discarded
  • Типы Types
    • Вложенные типы, объявленные в классе. Nested types declared by the class

Специальные возможности Accessibility

Каждый член класса имеет определенный уровень доступности. Он определяет, из какой области программы можно обращаться к этому члену. Each member of a class has an associated accessibility, which controls the regions of program text that are able to access the member. Существует шесть уровней доступности. There are six possible forms of accessibility. Они кратко описаны ниже. These are summarized below.

  • public
    • Доступ не ограничен. Access not limited
  • protected
    • Доступ возможен из этого класса и из классов, унаследованных от него. Access limited to this class or classes derived from this class
  • internal
    • Доступ ограничен только текущей сборкой (.exe, .dll и т. д.). Access limited to the current assembly (.exe, .dll, etc.)
  • protected internal
    • Доступ ограничен содержащим классом, классами, которые являются производными от содержащего класса, либо классами в той же сборке Access limited to the containing class, classes derived from the containing class, or classes within the same assembly
  • private
    • Доступ возможен только из этого класса. Access limited to this class
  • private protected
    • Доступ ограничен содержащим классом или классами, которые являются производными от содержащего типа в той же сборке Access limited to the containing class or classes derived from the containing type within the same assembly

Параметры типа Type parameters

Определение класса может задать набор параметров типа. Список имен параметров типа указывается в угловых скобках после имени класса. A class definition may specify a set of type parameters by following the class name with angle brackets enclosing a list of type parameter names. Параметры типа можно использовать в теле класса в определениях, описывающих члены класса. The type parameters can then be used in the body of the class declarations to define the members of the class. В следующем примере для класса Pair заданы параметры типа TFirst и TSecond : In the following example, the type parameters of Pair are TFirst and TSecond :

Тип класса, для которого объявлены параметры типа, называется универсальным типом класса. A class type that is declared to take type parameters is called a generic class type. Типы структуры, интерфейса и делегата также могут быть универсальными. Struct, interface and delegate types can also be generic. Если вы используете универсальный класс, необходимо указать аргумент типа для каждого параметра типа, вот так: When the generic class is used, type arguments must be provided for each of the type parameters:

Универсальный тип, для которого указаны аргументы типа, как Pair в примере выше, называется сконструированным типом. A generic type with type arguments provided, like Pair above, is called a constructed type.

базовых классов; Base classes

В объявлении класса можно указать базовый класс, включив имя базового класса после имени класса и параметров типа, и отделив его двоеточием. A class declaration may specify a base class by following the class name and type parameters with a colon and the name of the base class. Если спецификация базового класса не указана, класс наследуется от типа object . Omitting a base class specification is the same as deriving from type object . В следующем примере Point3D имеет базовый класс Point , а Point — базовый класс object : In the following example, the base class of Point3D is Point , and the base class of Point is object :

Класс наследует члены базового класса. A class inherits the members of its base class. Наследование означает, что класс неявно содержит все члены своего базового класса, за исключением конструкторов экземпляра, статических конструкторов и методов завершения базового класса. Inheritance means that a class implicitly contains all members of its base class, except for the instance and static constructors, and the finalizers of the base class. Производный класс может дополнить наследуемые элементы новыми элементами, но он не может удалить определение для наследуемого члена. A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member. В предыдущем примере Point3D наследует поля x и y из Point , и каждый экземпляр Point3D содержит три поля: x , y и z . In the previous example, Point3D inherits the x and y fields from Point , and every Point3D instance contains three fields, x , y , and z .

Используется неявное преобразование из типа класса к любому из типов соответствующего базового класса. An implicit conversion exists from a class type to any of its base class types. Это означает, что переменная типа класса может ссылаться как на экземпляр этого класса, так и на экземпляры любого производного класса. Therefore, a variable of a class type can reference an instance of that class or an instance of any derived class. Например, если мы используем описанные выше объявления классов, то переменная типа Point может ссылаться на Point или Point3D : For example, given the previous class declarations, a variable of type Point can reference either a Point or a Point3D :

Поля Fields

Поле является переменной, связанной с определенным классом или экземпляром класса. A field is a variable that is associated with a class or with an instance of a class.

Поле, объявленное с модификатором static, является статическим. A field declared with the static modifier defines a static field. Статическое поле определяет строго одно место хранения. A static field identifies exactly one storage location. Независимо от того, сколько будет создано экземпляров этого класса, существует только одна копия статического поля. No matter how many instances of a class are created, there is only ever one copy of a static field.

Поле, объявленное без модификатора static, является полем экземпляра. A field declared without the static modifier defines an instance field. Каждый экземпляр класса содержит отдельные копии всех полей экземпляра, определенных для этого класса. Every instance of a class contains a separate copy of all the instance fields of that class.

В следующем примере каждый экземпляр класса Color содержит отдельную копию полей экземпляра r , g и b , но для каждого из статических полей Black , White , Red , Green и Blue существует только одна копия: In the following example, each instance of the Color class has a separate copy of the r , g , and b instance fields, but there is only one copy of the Black , White , Red , Green , and Blue static fields:

Как показано в предыдущем примере, можно объявить поля только для чтения, используя модификатор readonly . As shown in the previous example, read-only fields may be declared with a readonly modifier. Присвоение значения полю readonly может происходить только при объявлении этого поля или в конструкторе этого класса. Assignment to a readonly field can only occur as part of the field’s declaration or in a constructor in the same class.

Методы Methods

Метод — это член, реализующий вычисление или действие, которое может выполнять объект или класс. A method is a member that implements a computation or action that can be performed by an object or class. Доступ к статическим методам осуществляется через класс. Static methods are accessed through the class. Доступ к методам экземпляра осуществляется через экземпляр класса. Instance methods are accessed through instances of the class.

Для метода можно определить список параметров, которые представляют переданные методу значения или ссылки на переменные, а также возвращаемый тип, который задает тип значения, вычисляемого и возвращаемого методом. Methods may have a list of parameters, which represent values or variable references passed to the method, and a return type, which specifies the type of the value computed and returned by the method. Если метод не возвращает значений, для него устанавливается возвращаемый тип void . A method’s return type is void if it does not return a value.

Как и типы, методы могут иметь набор параметров типа, для которых при вызове метода необходимо указывать аргументы типа. Like types, methods may also have a set of type parameters, for which type arguments must be specified when the method is called. В отличие от типов, аргументы типа зачастую могут выводиться из аргументов вызова метода, и тогда их не обязательно задавать явным образом. Unlike types, the type arguments can often be inferred from the arguments of a method call and need not be explicitly given.

Сигнатура метода должна быть уникальной в пределах класса, в котором объявлен этот метод. The signature of a method must be unique in the class in which the method is declared. Сигнатура метода включает имя метода, количество параметров типа, а также количество, модификаторы и типы параметров метода. The signature of a method consists of the name of the method, the number of type parameters and the number, modifiers, and types of its parameters. Сигнатура метода не включает возвращаемый тип. The signature of a method does not include the return type.

Параметры Parameters

Параметры позволяют передать в метод значения или ссылки на переменные. Parameters are used to pass values or variable references to methods. Фактические значения параметрам метода присваиваются на основе аргументов, заданных при вызове метода. The parameters of a method get their actual values from the arguments that are specified when the method is invoked. Существует четыре типа параметров: параметры значения, ссылочные параметры, параметры вывода и массивы параметров. There are four kinds of parameters: value parameters, reference parameters, output parameters, and parameter arrays.

Параметр значения используется для передачи входных аргументов. A value parameter is used for passing input arguments. Параметр значения сопоставляется с локальной переменной, которая получит начальное значение из значения аргумента, переданного в этом параметре. A value parameter corresponds to a local variable that gets its initial value from the argument that was passed for the parameter. Изменения параметра значения не влияют на аргумент, переданный для этого параметра. Modifications to a value parameter do not affect the argument that was passed for the parameter.

Параметры значения можно сделать необязательными, указав для них значения по умолчанию. Тогда соответствующие аргументы можно не указывать. Value parameters can be optional, by specifying a default value so that corresponding arguments can be omitted.

Ссылочный параметр используется для передачи аргументов по ссылке. A reference parameter is used for passing arguments by reference. Аргумент, передаваемый в качестве ссылочного параметра, должен представлять собой переменную с определенным значением. При выполнении метода ссылочный параметр указывает на то же место хранения, в котором размещена переменная аргумента. The argument passed for a reference parameter must be a variable with a definite value, and during execution of the method, the reference parameter represents the same storage location as the argument variable. Чтобы объявить ссылочный параметр, используйте модификатор ref . A reference parameter is declared with the ref modifier. Следующий пример кода демонстрирует использование параметров ref . The following example shows the use of ref parameters.

Параметр вывода используется для передачи аргументов по ссылке. An output parameter is used for passing arguments by reference. Он похож на ссылочный параметр, однако не требует явно присваивать значение аргумента, предоставляемого вызывающим объектом. It’s similar to a reference parameter, except that it doesn’t require that you explicitly assign a value to the caller-provided argument. Чтобы объявить параметр вывода, используйте модификатор out . An output parameter is declared with the out modifier. В следующем примере показано использование параметров out с помощью синтаксиса, появившегося в C# 7. The following example shows the use of out parameters using the syntax introduced in C# 7.

Массив параметров позволяет передавать в метод переменное число аргументов. A parameter array permits a variable number of arguments to be passed to a method. Чтобы объявить массив параметров, используйте модификатор params . A parameter array is declared with the params modifier. Массив параметров может быть только последним параметром в методе. Для него можно использовать только тип одномерного массива. Only the last parameter of a method can be a parameter array, and the type of a parameter array must be a single-dimensional array type. В качестве примера правильного использования массива параметров можно назвать методы Write и WriteLine, реализованные в классе System.Console. The Write and WriteLine methods of the System.Console class are good examples of parameter array usage. Ниже представлены объявления этих методов. They are declared as follows.

Внутри метода массив параметров полностью идентичен обычному параметру типа массив. Within a method that uses a parameter array, the parameter array behaves exactly like a regular parameter of an array type. Но зато при вызове метода, использующего массив параметров, ему можно передать либо один аргумент типа массив, либо любое количество аргументов типа элемент для массива параметров. However, in an invocation of a method with a parameter array, it is possible to pass either a single argument of the parameter array type or any number of arguments of the element type of the parameter array. В последнем случае экземпляр массива автоматически создается и инициализируется с заданными аргументами. In the latter case, an array instance is automatically created and initialized with the given arguments. Код из этого примера. This example

. эквивалентен следующей конструкции: is equivalent to writing the following.

Тело метода и локальные переменные Method body and local variables

Тело метода содержит инструкции, которые будут выполнены при вызове метода. A method’s body specifies the statements to execute when the method is invoked.

В теле метода можно объявлять переменные, относящиеся к выполнению этого метода. A method body can declare variables that are specific to the invocation of the method. Такие переменные называются локальными переменными. Such variables are called local variables. В объявлении локальной переменной нужно указать имя типа и имя переменной. Также можно задать ее начальное значение. A local variable declaration specifies a type name, a variable name, and possibly an initial value. Следующий пример кода объявляет локальную переменную i с нулевым начальным значением, и еще одну локальную переменную j без начального значения. The following example declares a local variable i with an initial value of zero and a local variable j with no initial value.

C# требует, чтобы локальной переменной было явно присвоено значение, прежде чем можно будет получить это значение. C# requires a local variable to be definitely assigned before its value can be obtained. Например, если в предложенное выше объявление i не включить начальное значение, компилятор сообщит об ошибке при последующем использовании i , поскольку для i нет явно присвоенного значения. For example, if the declaration of the previous i did not include an initial value, the compiler would report an error for the subsequent usages of i because i would not be definitely assigned at those points in the program.

Метод может использовать инструкцию return , чтобы вернуть управление вызывающему объекту. A method can use return statements to return control to its caller. Если метод возвращает void , в нем нельзя использовать инструкцию return с выражением. In a method returning void , return statements cannot specify an expression. В методе, выходное значение которого имеет любой другой тип, инструкции return должны содержать выражение, которое вычисляет возвращаемое значение. In a method returning non-void, return statements must include an expression that computes the return value.

Статические методы и методы экземпляра Static and instance methods

Метод, объявленный с модификатором static, является статическим методом. A method declared with a static modifier is a static method. Статический метод не работает с конкретным экземпляром и может напрямую обращаться только к статическим членам. A static method does not operate on a specific instance and can only directly access static members.

Метод, объявленный без модификатора static, является методом экземпляра. A method declared without a static modifier is an instance method. Метод экземпляра работает в определенном экземпляре и может обращаться как к статическим методам, так и к методам этого экземпляра. An instance method operates on a specific instance and can access both static and instance members. В методе можно напрямую обратиться к экземпляру, для которого этот метод был вызван, используя дескриптор this . The instance on which an instance method was invoked can be explicitly accessed as this . Использование this в статическом методе приводит к ошибке. It is an error to refer to this in a static method.

Следующий класс Entity содержит статические члены и члены экземпляра. The following Entity class has both static and instance members.

Каждый экземпляр Entity содержит серийный номер (и может содержать другие данные, которые здесь не показаны). Each Entity instance contains a serial number (and presumably some other information that is not shown here). Конструктор объекта Entity (который рассматривается как метод экземпляра) задает для нового экземпляра следующий доступный серийный номер. The Entity constructor (which is like an instance method) initializes the new instance with the next available serial number. Поскольку конструктор является членом экземпляра, он может обращаться как к полю экземпляра serialNo , так и к статическому полю nextSerialNo . Because the constructor is an instance member, it is permitted to access both the serialNo instance field and the nextSerialNo static field.

Статические методы GetNextSerialNo и SetNextSerialNo могут обращаться к статическому полю nextSerialNo , но прямое обращение из них к полю экземпляра serialNo приводит к ошибке. The GetNextSerialNo and SetNextSerialNo static methods can access the nextSerialNo static field, but it would be an error for them to directly access the serialNo instance field.

В следующем примере показано использование класса Entity. The following example shows the use of the Entity class.

Обратите внимание, что статические методы SetNextSerialNo и GetNextSerialNo вызываются для класса, а метод экземпляра GetSerialNo вызывается для экземпляров класса. Note that the SetNextSerialNo and GetNextSerialNo static methods are invoked on the class whereas the GetSerialNo instance method is invoked on instances of the class.

Виртуальные, переопределяющие и абстрактные методы Virtual, override, and abstract methods

Если объявление метода экземпляра включает модификатор virtual , такой метод называется виртуальным методом. When an instance method declaration includes a virtual modifier, the method is said to be a virtual method. Если модификатор virtual отсутствует, метод считается невиртуальным. When no virtual modifier is present, the method is said to be a nonvirtual method.

При вызове виртуального метода могут быть вызваны разные его реализации в зависимости от того, какой тип среды выполнения имеет экземпляр, для которого вызван этот метод. When a virtual method is invoked, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke. При вызове невиртуального метода решающим фактором является тип во время компиляции для этого экземпляра. In a nonvirtual method invocation, the compile-time type of the instance is the determining factor.

Виртуальный метод можно переопределить в производном классе. A virtual method can be overridden in a derived class. Если объявление метода экземпляра содержит модификатор override, этот метод переопределяет унаследованный виртуальный метод с такой же сигнатурой. When an instance method declaration includes an override modifier, the method overrides an inherited virtual method with the same signature. Изначальное объявление виртуального метода создает новый метод, а переопределение этого метода создает специализированный виртуальный метод с новой реализацией взамен унаследованного виртуального метода. Whereas a virtual method declaration introduces a new method, an override method declaration specializes an existing inherited virtual method by providing a new implementation of that method.

Абстрактным методом называется виртуальный метод без реализации. An abstract method is a virtual method with no implementation. Абстрактный метод объявляется с модификатором abstract. Его можно объявить только в классе, который также объявлен абстрактным. An abstract method is declared with the abstract modifier and is permitted only in a class that is also declared abstract. Абстрактный метод должен обязательно переопределяться в каждом производном классе, не являющемся абстрактным. An abstract method must be overridden in every non-abstract derived class.

Следующий пример кода объявляет абстрактный класс Expression , который представляет узел дерева выражений, а также три производных класса: Constant , VariableReference и Operation , которые реализуют узлы дерева выражений для констант, ссылок на переменные и арифметических операций. The following example declares an abstract class, Expression , which represents an expression tree node, and three derived classes, Constant , VariableReference , and Operation , which implement expression tree nodes for constants, variable references, and arithmetic operations. (Они похожи на типы дерева выражений, но их нельзя путать.) (This is similar to, but not to be confused with the expression tree types).

Четыре приведенных выше класса можно использовать для моделирования арифметических выражений. The previous four classes can be used to model arithmetic expressions. Например, с помощью экземпляров этих классов выражение x + 3 можно представить следующим образом. For example, using instances of these classes, the expression x + 3 can be represented as follows.

Метод Evaluate экземпляра Expression вызывается для вычисления данного выражения и создает значение double . The Evaluate method of an Expression instance is invoked to evaluate the given expression and produce a double value. Этот метод принимает аргумент Dictionary , который содержит имена переменных (в качестве ключей записей) и значения переменных (в качестве значений записей). The method takes a Dictionary argument that contains variable names (as keys of the entries) and values (as values of the entries). Так как Evaluate — абстрактный метод, то в неабстрактных классах, производных от Expression , необходимо переопределить Evaluate . Because Evaluate is an abstract method, non-abstract classes derived from Expression must override Evaluate .

В Constant реализация метода Evaluate просто возвращает хранимую константу. A Constant ‘s implementation of Evaluate simply returns the stored constant. В VariableReference реализация этого метода выполняет поиск имени переменной в словаре и возвращает полученное значение. A VariableReference ‘s implementation looks up the variable name in the dictionary and returns the resulting value. В Operation реализация этого метода сначала вычисляет левый и правый операнды (рекурсивно вызывая их методы Evaluate ), а затем выполняет предоставленную арифметическую операцию. An Operation ‘s implementation first evaluates the left and right operands (by recursively invoking their Evaluate methods) and then performs the given arithmetic operation.

В следующей программе классы Expression используются для вычисления выражения x * (y + 2) с различными значениями x и y . The following program uses the Expression classes to evaluate the expression x * (y + 2) for different values of x and y .

Перегрузка методов Method overloading

Перегрузка метода позволяет использовать в одном классе несколько методов с одинаковыми именами, если они имеют уникальные сигнатуры. Method overloading permits multiple methods in the same class to have the same name as long as they have unique signatures. Когда при компиляции встречается вызов перегруженного метода, компилятор использует принцип разрешения перегрузки, чтобы определить, какой из методов следует вызвать. When compiling an invocation of an overloaded method, the compiler uses overload resolution to determine the specific method to invoke. Разрешение перегрузки выбирает из методов тот, который лучше всего соответствует предоставленным аргументам, или возвращает ошибку, если не удается выбрать конкретный подходящий метод. Overload resolution finds the one method that best matches the arguments or reports an error if no single best match can be found. В следующем примере показано, как работает разрешение перегрузки. The following example shows overload resolution in effect. Комментарий к каждому вызову метода UsageExample подсказывает, какой из методов будет вызван для этой строки. The comment for each invocation in the UsageExample method shows which method is actually invoked.

Как видно из этого примера, вы всегда можете выбрать конкретный метод, явным образом приведя типы аргументов к соответствующим типам параметров, и (или) явно предоставив аргументы нужного типа. As shown by the example, a particular method can always be selected by explicitly casting the arguments to the exact parameter types and/or explicitly supplying type arguments.

Другие функции-члены Other function members

Все члены класса, содержащие исполняемый код, совокупно называются функции-члены. Members that contain executable code are collectively known as the function members of a class. В предыдущем разделе мы рассмотрели основные варианты методов, используемых как функции-члены. The preceding section describes methods, which are the primary kind of function members. В этом разделе описываются другие типы функций-членов, поддерживаемые в языке C#: конструкторы, свойства, индексаторы, события, операторы и методы завершения. This section describes the other kinds of function members supported by C#: constructors, properties, indexers, events, operators, and finalizers.

Ниже приведен универсальный класс с именем MyList , который реализует расширяемый список объектов. The following shows a generic class called MyList , which implements a growable list of objects. Этот класс содержит несколько наиболее распространенных типов функций-членов. The class contains several examples of the most common kinds of function members.

В этом примере создается класс MyList , который отличается от стандартного System.Collections.Generic.List в .NET. This example creates a MyList class, which is not the same as the .NET standard System.Collections.Generic.List . Здесь показаны основные понятия, необходимые для этого руководства, но они не заменят собой этот класс. It does illustrate the concepts needed for this tour, but is not a replacement for that class.

Конструкторы Constructors

C# поддерживает конструкторы экземпляров и статические конструкторы. C# supports both instance and static constructors. Конструктор экземпляра является членом, который реализует действия для инициализации нового экземпляра класса. An instance constructor is a member that implements the actions required to initialize an instance of a class. Статический конструктор является членом, который реализует действия для инициализации самого класса при первоначальной его загрузке. A static constructor is a member that implements the actions required to initialize a class itself when it is first loaded.

Конструктор объявляется в виде метода без возвращаемого типа, имя которого совпадает с именем класса, в котором он определен. A constructor is declared like a method with no return type and the same name as the containing class. Если объявление конструктора содержит модификатор static, создается статический конструктор. If a constructor declaration includes a static modifier, it declares a static constructor. В противном случае это объявление считается конструктором экземпляра. Otherwise, it declares an instance constructor.

Конструкторы экземпляров можно перегружать, и для них можно указать необязательные параметры. Instance constructors can be overloaded and can have optional parameters. Например, класс MyList объявляет один конструктор экземпляра с одним необязательным параметром int . For example, the MyList class declares one instance constructor with a single optional int parameter. Конструкторы экземпляров вызываются с помощью оператора new . Instance constructors are invoked using the new operator. Следующий пример кода выделяет два экземпляра MyList с помощью конструкторов класса MyList : один с необязательным аргументом, а второй — без. The following statements allocate two MyList instances using the constructor of the MyList class with and without the optional argument.

В отличие от других членов конструкторы экземпляров не наследуются, и класс не имеет конструкторов экземпляров, кроме объявленных в самом этом классе. Unlike other members, instance constructors are not inherited, and a class has no instance constructors other than those actually declared in the class. Если в классе не объявлен конструктор экземпляра, для него автоматически создается пустой конструктор без параметров. If no instance constructor is supplied for a class, then an empty one with no parameters is automatically provided.

Свойства Properties

Свойства естественным образом дополняют поля. Properties are a natural extension of fields. И те, и другие являются именованными членами со связанными типами, и для доступа к ним используется одинаковый синтаксис. Both are named members with associated types, and the syntax for accessing fields and properties is the same. Однако свойства, в отличие от полей, не указывают места хранения. However, unlike fields, properties do not denote storage locations. Вместо этого свойства содержат методы доступа, в которых описаны инструкции для выполнения при чтении или записи значений. Instead, properties have accessors that specify the statements to be executed when their values are read or written.

Свойство объявляется так же, как поле, за исключением того, что объявление заканчивается не точкой с запятой, а парой разделителей < и >, между которыми указаны акцессоры get для чтения и (или) set для записи. A property is declared like a field, except that the declaration ends with a get accessor and/or a set accessor written between the delimiters < and >instead of ending in a semicolon. Свойство, для которого определены акцессоры get и set, является свойством для чтения и записи. Если в свойстве есть только акцессор get, оно является свойством только для чтения, и если только акцессор set — свойством только для записи. A property that has both a get accessor and a set accessor is a read-write property, a property that has only a get accessor is a read-only property, and a property that has only a set accessor is a write-only property.

Акцессор get оформляется как метод без параметров, у которого тип возвращаемого значения совпадает с типом, установленным для этого свойства. A get accessor corresponds to a parameterless method with a return value of the property type. Во всех ситуациях, кроме использования в качестве назначения в операторе присваивания, для вычисления значения свойства вызывается акцессор get. Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property.

Илон Маск рекомендует:  Средства обеспечения информационной безопасности вашего дома и офиса

Метод доступа set соответствует методу с одним именованным значением параметра и без возвращаемого типа. A set accessor corresponds to a method with a single parameter named value and no return type. При ссылке на свойство в качестве назначения в операторе присваивания или в качестве операнда для ++ или — вызывается метод доступа set с аргументом, предоставляющим новое значение. When a property is referenced as the target of an assignment or as the operand of ++ or —, the set accessor is invoked with an argument that provides the new value.

Класс MyList объявляет два свойства: Count (только для чтения) и Capacity (только для записи). The MyList class declares two properties, Count and Capacity , which are read-only and read-write, respectively. Пример использования этих свойств приведен ниже. The following is an example of use of these properties:

Как и в отношении полей и методов, C# поддерживает свойства экземпляра и статические свойства. Similar to fields and methods, C# supports both instance properties and static properties. Статические свойства объявляются с модификатором static, а свойства экземпляра — без него. Static properties are declared with the static modifier, and instance properties are declared without it.

Акцессоры свойства могут быть виртуальными. The accessor(s) of a property can be virtual. Если объявление свойства содержит модификатор virtual , abstract или override , этот модификатор применяется к акцессорам свойства. When a property declaration includes a virtual , abstract , or override modifier, it applies to the accessor(s) of the property.

Индексаторы Indexers

Индексатор является членом, позволяющим индексировать объекты так, как будто они включены в массив. An indexer is a member that enables objects to be indexed in the same way as an array. Индексатор объявляется так же, как свойство, за исключением того, что именем элемента является this , а за этим именем следует список параметров, находящийся между разделителями [ и ] . An indexer is declared like a property except that the name of the member is this followed by a parameter list written between the delimiters [ and ] . Эти параметры доступны в акцессорах индексатора. The parameters are available in the accessor(s) of the indexer. Как и свойства, можно объявить индексаторы для чтения и записи, только для чтения или только для записи. Кроме того, поддерживаются виртуальные акцессоры индексатора. Similar to properties, indexers can be read-write, read-only, and write-only, and the accessor(s) of an indexer can be virtual.

Класс MyList объявляет один индексатор для чтения и записи, который принимает параметр int . The MyList class declares a single read-write indexer that takes an int parameter. Индексатор позволяет индексировать экземпляры MyList значениями с типом int . The indexer makes it possible to index MyList instances with int values. Например: For example:

Индексаторы можно перегружать, то есть в одном классе можно объявить несколько индексаторов, если у них различаются количество или типы параметров. Indexers can be overloaded, meaning that a class can declare multiple indexers as long as the number or types of their parameters differ.

События Events

Событие — это член, с помощью которого класс или объект предоставляют уведомления. An event is a member that enables a class or object to provide notifications. Объявление события выглядит так же, как объявление поля, но содержит ключевое слово event и обязано иметь тип делегата. An event is declared like a field except that the declaration includes an event keyword and the type must be a delegate type.

В классе, который объявляет член события, это событие действует, как обычное поле с типом делегата (если это событие не является абстрактным и не объявляет акцессоры). Within a class that declares an event member, the event behaves just like a field of a delegate type (provided the event is not abstract and does not declare accessors). Это поле хранит ссылку на делегат, который представляет добавленные к событию обработчики событий. The field stores a reference to a delegate that represents the event handlers that have been added to the event. Если обработчики событий отсутствуют, это поле имеет значение null . If no event handlers are present, the field is null .

Класс MyList объявляет один член события с именем Changed , который обрабатывает добавление нового элемента. The MyList class declares a single event member called Changed , which indicates that a new item has been added to the list. Событие Changed вызывается виртуальным методом OnChanged , который сначала проверяет, не имеет ли это событие значение null (это означает, что обработчики отсутствуют). The Changed event is raised by the OnChanged virtual method, which first checks whether the event is null (meaning that no handlers are present). Концепция создания события в точности соответствует вызову делегата, представленного этим событием. Это позволяет обойтись без особой языковой конструкции для создания событий. The notion of raising an event is precisely equivalent to invoking the delegate represented by the event—thus, there are no special language constructs for raising events.

Клиенты реагируют на события посредством обработчиков событий. Clients react to events through event handlers. Обработчики событий можно подключать с помощью оператора += и удалять с помощью оператора -= . Event handlers are attached using the += operator and removed using the -= operator. Следующий пример кода подключает обработчик события Changed к событию MyList . The following example attaches an event handler to the Changed event of a MyList .

Для более сложных сценариев, требующих контроля над базовым хранилищем события, в объявлении события можно явным образом предоставить акцессоры add и remove . Они будут действовать примерно так же, как акцессор set для свойства. For advanced scenarios where control of the underlying storage of an event is desired, an event declaration can explicitly provide add and remove accessors, which are somewhat similar to the set accessor of a property.

Операторы Operators

Оператор является членом, который определяет правила применения определенного выражения к экземплярам класса. An operator is a member that defines the meaning of applying a particular expression operator to instances of a class. Вы можете определить операторы трех типов: унарные операторы, двоичные операторы и операторы преобразования. Three kinds of operators can be defined: unary operators, binary operators, and conversion operators. Все операторы объявляются с модификаторами public и static . All operators must be declared as public and static .

Класс MyList объявляет два оператора: operator == и operator != . Это позволяет определить новое значение для выражений, которые применяют эти операторы к экземплярам MyList . The MyList class declares two operators, operator == and operator != , and thus gives new meaning to expressions that apply those operators to MyList instances. В частности, эти операторы определяют, что равенство двух экземпляров MyList проверяется путем сравнения всех содержащихся в них объектов с помощью определенных для них методов Equals. Specifically, the operators define equality of two MyList instances as comparing each of the contained objects using their Equals methods. Следующий пример кода использует оператор == для сравнения двух экземпляров MyList . The following example uses the == operator to compare two MyList instances.

Первый Console.WriteLine выводит True , поскольку два списка содержат одинаковое число объектов с одинаковыми значениями в том же порядке. The first Console.WriteLine outputs True because the two lists contain the same number of objects with the same values in the same order. Если бы в MyList не было определения operator == , первый Console.WriteLine возвращал бы False , поскольку a и b указывают на различные экземпляры MyList . Had MyList not defined operator == , the first Console.WriteLine would have output False because a and b reference different MyList instances.

Методы завершения Finalizers

Метод завершения является членом, который реализует действия для завершения существования экземпляра класса. A finalizer is a member that implements the actions required to finalize an instance of a class. Методы завершения не могут иметь параметры, не могут содержать модификаторы доступа и их нельзя вызвать явным образом. Finalizers cannot have parameters, they cannot have accessibility modifiers, and they cannot be invoked explicitly. Метод завершения для экземпляра вызывается автоматически в процессе сборки мусора. The finalizer for an instance is invoked automatically during garbage collection.

Сборщик мусора имеет широкую степень свободы в выборе времени уничтожения объектов и вызова методов завершения. The garbage collector is allowed wide latitude in deciding when to collect objects and run finalizers. В частности, время вызова методов завершения не является детерминированным, и эти методы могут выполняться в любом потоке. Specifically, the timing of finalizer invocations is not deterministic, and finalizers may be executed on any thread. По этим и некоторым другим причинам методы завершения следует использовать в классах только в крайнем случае, когда невозможны другие решения. For these and other reasons, classes should implement finalizers only when no other solutions are feasible.

Уничтожение объектов лучше контролировать с помощью инструкции using . The using statement provides a better approach to object destruction.

Понятие класса, экземпляра класса и объекта в ООП.

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

Из предыдущего видео мы знаем, что самым главным действующим лицом в объектно-ориентированном подходе программирования является объект, у которого есть какие-то свойства (т.е. характеристики этого объекта), значения этих свойств и методы, те действия, которые этот объект может совершать.

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

1) Его нужно описать. Какие свойства и методы есть у этого объекта.

2) Его нужно создать

Что значит описать объект? Описание объекта – это определение его свойств и методов, которые этот объект может принимать. Т.е. мы начинаем создавать программу и пишем, что у нас будет некий объект (например, Employee или Работник).

У этого объекта будет свойства first_name (имя), last_name (фамилия), age (возраст).

Этот объект при запросе к нему может выдавать его имя (getFirstName()), фамилию getLastName()), возраст (getAge()).

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

Что же значит создать объект?

Создание объекта – это некий процесс обращения к конкретному экземпляру описанного объекта. После описания объекта он имеет некую абстрактную форму и когда мы обращаемся к какому-то конкретному работнику, к какому-то конкретному экземпляру этого описания: работник 1, работник 2, работник 3. У нас может быть множество работников, которые соответствуют этой схеме.

Когда мы с вами обращаемся к какому-то конкретному экземпляру этой сущности работника, то мы создаем некий объект, т.е. некий экземпляр существующего описания этого объекта.

Тот программный код, который содержит описательную часть объекта, он называется класс. Часть, в которой мы описываем объект – это называется классом. Т.е. класс – это некая сущность, которая описываем, что будут собой представлять все те экземпляры, которые будут создаваться из этого класса.

Экземпляры классов = объекты.

Объект – это просто что-то конкретное, а класс – это некое абстрактное понятие, которое просто содержит описательную часть.

Класс – это что-то вроде чертежа объекта, т.е. прежде чем мы создадим какую-то деталь, проектировщик должен сделать ее чертеж. Написать, что у этой детали будут такие-то свойства (высота, ширина, длина). Когда чертеж будет выполнен, мы отдаем его какому-то мастеру, и он уже создает экземпляры из этого класса. Деталей, которые могут быть созданы из этого чертежа огромное количество.

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

И нужно понимать, что

Чертеж НЕ равно деталь

Класс НЕ равно объект

Общий алгоритм работы с объектно-ориентированным подходом в программировании:

  1. Создали класс
  2. Создали экземпляр класса (объект)
  3. Обращаемся к свойствам и методам экземпляра класса.

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

Чтобы оставить сообщение, зарегистрируйтесь/войдите на сайт через:

Или зарегистрируйтесь через социальные сети:

Объектно-ориентированное программирование

Содержание

Общие замечания [ править ]

Объектно-ориентированная парадигма программирования не нова. Её истоки восходят к Симуле-67, хотя впервые она была полностью реализована в Smalltalk-80. ООП (Объектно-ориентированное программирование) приобрело популярность во второй половине 80-х вместе с такими языками, как С++, Objective C (другое расширение C), Object Pascal и Turbo Pascal, CLOS (объектно-ориентированное расширение Lisp’a), Eiffel, Ada (в её последних воплощениях) и недавно — в Java. В этой статье внимание сосредоточено на C++, Object Pascal и Java, иногда упоминаются и другие языки.

Ключевые черты ООП хорошо известны:

  1. Первая — инкапсуляция — это определение классов — пользовательских типов данных, объединяющих своё содержимое в единый тип и реализующих некоторые операции или методы над ним. Классы обычно являются основой модульности, инкапсуляции и абстракции данных в языках ООП.
  2. Вторая ключевая черта, — наследование — способ определения нового типа, когда новый тип наследует элементы (свойства и методы) существующего, модифицируя или расширяя их. Это способствует выражению специализации и генерализации.
  3. Третья черта, известная как полиморфизм, позволяет единообразно ссылаться на объекты различных классов (обычно внутри некоторой иерархии). Это делает классы ещё удобнее и облегчает расширение и поддержку программ, основанных на них.

Инкапсуляция, наследование и полиморфизм — фундаментальные свойства, которыми должен обладать язык, претендующий называться объектно-ориентированным (языки, не имеющие наследования и полиморфизма, но имеющие только классы, обычно называются основанными на классах). Различные ОО языки используют совершенно разные подходы. Мы можем различать ОО языки, сравнивая механизм контроля типов, способность поддерживать различные программные модели и то, какие объектные модели они поддерживают.

Алан Кей в свое время вывел пять основных черт языка Smalltalk — первого удачного ОО языка:

  1. Все является объектом. Объект как хранит информацию, так и способен ее преобразовывать. В принципе любой элемент решаемой задачи (дом, собака, услуга, химическая реакция, город, космический корабль и т. д.) может представлять собой объект. Объект можно представить себе как швейцарский нож: он является набором различных ножей и «открывашек» (хранение), но в то же самое время им мы можем резать или открывать что-либо (преобразование).
  2. Программа — совокупность объектов, указывающих друг другу что делать. Для обращения к одному объекту другой объект «посылает ему сообщение». Как вариант возможно и «ответное сообщение». Программу можно представить себе как совокупность к примеру 3 объектов: писателя, ручки и листа бумаги. Писатель «посылает сообщение» ручке, которая в свою очередь «посылает сообщение» листу бумаги — в результате мы видим текст (посыл сообщения от листа к писателю).
  3. Каждый объект имеет свою собственную «память» состоящую из других объектов. Таким образом программист может скрыть сложность программы за довольно простыми объектами. К примеру, дом (достаточно сложный объект) состоит из дверей, комнат, окон, проводки и отопления. Дверь, в свою очередь, может состоять из собственно полотна, ручки, замка и петель. Проводка тоже состоит из проводов, розеток и, к примеру, щитка.
  4. У каждого объекта есть тип. Иногда тип называют еще и классом. Класс (тип) определяет какие сообщения объекты могут посылать друг другу. Например, аккумуляторная батарея может передавать электролампе ток, а вот импульс или физическое усилие — нет.
  5. Все объекты одного типа могут получать одинаковые сообщения. К примеру у нас есть 2 объекта: синяя и красная кружки. Обе разные по форме и материалу. Но из обеих мы можем пить (или не пить, если они пустые). В данном случае кружка — это тип объекта.

Самое лаконичное описание объекта предложил Буч: «Объект обладает состоянием, поведением и индивидуальностью».

Контроль во время компиляции и во время выполнения [ править ]

Языки программирования можно оценить по тому, насколько они строги к типам. Контроль типов включает проверку существования вызываемых методов, видов их параметров, проверку границ массивов и подобное.

C++, Java, и Object Pascal предпочитают более или менее тщательный контроль типов во время компиляции. С++, возможно, наименее точен в этом отношении (на что указывает, к примеру, возможность присвоения double к float), тогда как Java использует проверку типов наиболее широко. Это оттого, что C++ обеспечивает совместимость с Си, который не очень строго проверяет типы во время компиляции. Например, C и C++ считают, что все арифметические типы совместимы (хотя присвоение float целой переменной вызовет предупреждение компилятора). В Object Pascal и Java логическое значение не целое, а символ — еще один отличный и несовместимый тип.

Тот факт, что виртуальная машина Java интерпретирует байтовый код во время выполнения, не означает, что этот язык отказывается от проверки типов во время компиляции. Наоборот, в этом языке проверка наиболее тщательна. Другие ОО языки, такие как Smalltalk и CLOS, наоборот, склонны большинство проверок типов (если не все) осуществлять во время исполнения.

Чисто объектно-ориентированные и гибридные языки [ править ]

Различаются чистые и гибридные объектно-ориентированные языки. Чистые — языки, которые позволяют использовать только одну модель программирования — объектно-ориентированную. Можно объявлять классы и методы, но не можете завести глобальные переменные и обычные функции и процедуры старого типа.

Среди наших четырех языков только Java и C# являются чистыми ОО языками (как Eiffel и Smalltalk). На первый взгляд это кажется положительной идеей. Однако она ведет к тому, что вы используете кучу статических методов и статических данных, что не так уж отличается от использования глобальных функций и данных, за исключением более сложного синтаксиса. Чистые ОО языки дают преимущество новичкам в ООП, потому что программист вынужден использовать (и учить) модель ООП. C++ и Object Pascal, наоборот, — типичные примеры гибридных языков, которые позволяют программистам использовать при необходимости традиционный подход C или Pascal.

Smalltalk расширяет эту идею до уровня «объектирования» таких предопределенных типов данных, как целые и символы, а также языковых конструкций (таких как циклы). Это теоретически интересно, но сильно уменьшает эффективность. Java и C# останавливаются намного раньше, допуская присутствие простых не ОО типов данных (хотя имеются необязательные классы-обертки и для простых типов).

Простая объектная модель и ссылочно-объектная модель [ править ]

Свойство: Третий элемент, по которому различаются языки ООП — их объектная модель. Некоторые традиционные языки ООП позволяют программистам создавать объекты в стеке, в куче (в хипе — heap) или в статической памяти. В этих языках переменная типа класс соответствует объекту в памяти. Так работает C++.

В последнее время появилась тенденция использовать другую модель, часто называемую ссылочно-объектной моделью. В этой модели каждый объект динамически размещается в куче, а переменная типа класс фактически является ссылкой или хэндлом объекта в памяти (технически это нечто вроде указателя). Java и Object Pascal оба используют эту ссылочную модель. В C# используется преимущественно ссылочно-объектная модель, однако имеется возможность создавать т. н. структуры (по сути дела, структура здесь — специальная разновидность класса), объекты которых будут располагаться в стеке и статической памяти. Как мы увидим, вкратце это значит, что вам необходимо не забыть выделить память для объекта.

Классы, объекты и ссылки [ править ]

Свойство: Так как мы обсуждаем языки ООП, то после этого введения, начнём обсуждать классы и объекты. Я надеюсь, что каждый ясно понимает разницу между этими двумя терминами. В двух словах, класс — это тип данных, а объект — экземпляр типа класс. «Кружка» — это класс (тип). А уж которая, — синяя или красная, — это два разных объекта (экземпляра), типа «кружка». Как нам теперь использовать объекты в языках, использующих различные объектные модели?

C++: если у нас есть класс MyClass с методом MyMethod, мы можем написать:

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

Также возможно выделить память для объекта в куче и оперировать указателем на объект:

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

Прежде чем использовать объект, вы должны вызвать » new » для выделения под него памяти. Конечно, вы можете объявить и проинициализировать объект в одном предложении, избегая использования неинициализированных объектных хэндлов:

Object Pascal: использует подобный подход, но требует отдельных предложений для объявления и инициализации:

В C# работа с объектами классов и структур внешне выглядит похоже:

Но все же есть одно принципиальное отличие — память под объект структуры выделяется статически, поэтому вызов конструктора перед вызовом метода не является обязательным — объект уже существует, хотя его поля могут содержать «мусорные» значения.

Замечание: Если вам кажется, что ссылочно-объектная модель требует большей работы от программиста, обратите внимание на то, что в C++ вы часто должны использовать указатели и ссылки на объекты. Только используя указатели и ссылки, вы можете добиться полиморфизма. Ссылочно-объектная модель, наоборот, делает использование указателей подразумеваемым, скрывая от программиста сложность этого подхода. В Java, в частности, официально указателей нет, хотя они там повсюду. Только программисты не имеют над ними прямого контроля, и поэтому, из соображений безопасности, не могут попасть в произвольное место памяти.

Мусорная корзина [ править ]

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

C++: В C++ уничтожить объект, расположенный в стеке, довольно просто. С другой стороны, уничтожение объектов, созданных динамически, зачастую является сложной проблемой. Есть много решений, включая подсчет ссылок и «интеллектуальные» указатели, но ни один из них не даёт простого решения. Первое впечатление для C++ программистов, что использование ссылочно-объектной модели сделает ситуацию только хуже.

Java: Это, конечно, не касается Java, так как виртуальная машина запускает алгоритм сборки мусора (в фоновом процессе, согласно теории Java; или начинает этот процесс после того, как ненадолго остановит программу, как в большинстве реальных JVM). Сбор мусора происходит без участия программиста, но он может неблагоприятно влиять на эффективность выполнения приложения. Отсутствие явно записываемых деструкторов может приводить к ошибкам в завершающем коде (см. также главу о деструкторах и методе finalize() ниже).

OP: В Object Pascal, наоборот, нет механизма сбора мусора. Однако компоненты Delphi поддерживают идею владельца (owner) объекта: владелец становится ответственным за уничтожение всех объектов, которыми он владеет. Это делает управление уничтожением объекта очень простым и прямым. Delphi также использует механизм подсчёта ссылок для строк, динамических массивов и интерфейсов, освобождая объект в памяти, когда на него нет больше ссылок.

C#: В CLR (среде выполнения .NET) имеется сборщик мусора, аналогичный используемому в виртуальной машине Java. Поэтому программисту также не приходится заботиться об удалении созданных объектов. Однако, имеется специальный интерфейс IDisposable, которым «обозначаются» объекты, требующие ручной деинициализации (например, закрытия потоков ввода-вывода).

Определение новых классов [ править ]

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

C++: Вот C++ синтаксис определения простого класса:

А вот определение пользовательского метода-инициализатора:

Java: Синтаксис Java похож на синтаксис C++:

Основная разница состоит в том, что код каждого метода пишется там же, где он объявляется (при этом функции не становятся вставными (inline), как в C++), и в том, что вы можете инициализировать элементы данных класса. Фактически, если вы не сделаете этого, то Java проинициализирует все элементы данных за вас, используя значения по умолчанию.

OP: В Object Pascal синтаксис определения класса другой, но похожий скорее на C++, чем на Java:

Как видите, здесь есть синтаксические отличия: методы определяются с ключевыми словами function и procedure, методы без параметров не используют скобок, методы просто объявляются внутри определения класса, тогда как определяются позже, как это обычно делается в C++. Однако Pascal использует нотацию с точкой, а C++ — оператор :: (недоступный в Object Pascal и Java).

C# Синтаксис C# очень похож на Java (в данном примере идентичен):

Примечание: Доступ к текущему объекту. В ОО языках методы отличаются от глобальных функций тем, что у них присутствует скрытый параметр, ссылка или указатель на объект, с которым мы работаем. Просто эта ссылка на текущий объект по-разному называется. Это this в C++, Java и C#, Self в Object Pascal.

Создание и уничтожение объектов [ править ]

Конструкторы [ править ]

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

C++: В C++, так же, как и в Java, имя конструктора совпадает с именем класса. Если вы не определили никакого конструктора, компилятор синтезирует конструктор по умолчанию, добавляя его к классу. В обоих этих языках вы можете завести несколько конструкторов благодаря перегрузке функций.

Java: Всё работает как в C++, хотя конструкторы называются также инициализаторами. Это подчеркивает тот факт, что объект создаёт виртуальная машина Java, тогда как код, который вы пишете в конструкторе, просто инициализирует свежесозданный объект. (То же самое фактически происходит и в Object Pascal.)

OP: В Object Pascal вы используете специальное ключевое слово constructor и можете дать конструктору любое имя. Хотя Borland в Delphi 4 добавила поддержку перегрузки методов, программисты всё ещё дают разным конструкторам разные имена. В Object Pascal у каждого класса по умолчанию есть конструктор Create (наследуемый от TObject), если вы не перегрузите его конструктором с тем же именем и, возможно, другими параметрами. Этот конструктор, как мы увидим позднее, просто наследуется от общего базового класса.

C#: Синтаксис объявления конструктора очень похож на Java, а в нашем примере будет идентичен.

Деструкторы и финализация [ править ]

Свойство: Деструктор играет роль противоположную конструктору и обычно вызывается при уничтожении объекта. Если конструктор нужен большинству классов, то только некоторые из них нуждаются в деструкторе. Деструктор в основном используется для освобождения ресурсов, зарезервированных конструктором (или другими методами во время жизни объекта). Эти ресурсы включают память, файлы, базы данных, ресурсы ОС и т. д.

C++: деструкторы вызываются автоматически, когда объект выходит из области определения или когда вы удаляете объект, заведенный динамически. У каждого класса есть только один деструктор, который объявляется как

Class(), где Class — имя класса. Если объект создан в куче, то он не может быть автоматически удален и если не объявить деструктор явно в программе, то происходит утечка памяти (в Java данная проблема решена сборщиком мусора).

OP: деструкторы похожи на деструкторы C++. (Для деструкторов используется ключевое слово destructor [мое примечание — В. К.]) Object Pascal использует стандартный виртуальный деструктор, называемый Destroy. Этот деструктор вызывается стандартным методом Free. Все объекты динамические, поэтому предполагается, что вы вызовете Free для каждого объекта, созданного вами, если у того нет владельца, отвечающего за его уничтожение. Теоретически вы можете объявить несколько деструкторов, что имеет смысл, если вы можете вызывать деструкторы в своем коде (это не делается автоматически).

Java: В Java нет деструкторов. Объекты, на которые нет ссылок, уничтожаются сборщиком мусора, который работает в виде фоновой задачи и делает это автоматически (как описывалось ранее). Прежде чем уничтожать объект, сборщик мусора должен вызвать метод finalize(). Однако нет никакой гарантии, что этот метод вызывается в каждой JVM. По этой причине, если вам нужно освободить ресурсы, вы должны добавить какой-нибудь метод, и убедиться, что он вызывается (эти дополнительные усилия не нужны в других ОО языках).

C# Как и в виртуальной машине Java, в CLR используется автоматическая сборка мусора. Как было сказано выше, существует специальный интерфейс для объектов, требующих ручного освобождения ресурсов. В C# также можно создать метод вида

имя_класса(), который полностью аналогичен методу finalize() в Java.

Инкапсуляция (Private и Public) [ править ]

Свойство: Общим элементом всех трех языков является присутствие трех спецификаторов доступа, указывающих на различные уровни инкапсуляции класса: public, protected, и private. Public означает: видимый любым другим классом, protected означает: видимый производными классами, private означает: отсутствие видимости извне. В деталях, однако, есть различия.

C++: В C++ вы можете использовать ключевое слово friend для обхода инкапсуляции. Видимость по умолчанию для класса — private, для структур — public.

OP: В Object Pascal private и protected относятся только к классам других юнитов. В терминах C++, класс является дружественным для любого другого класса, определенного в том же юните (или файле исходного кода). В Delphi есть еще один модификатор доступа — published, который генерирует информацию времени выполнения (RTTI) об элементах.

Java: В Java отличие синтаксиса в том, что модификатор доступа повторяется для каждого элемента класса. А конкретнее, по умолчанию в Java используется friendly, это значит, что элемент видим для других классов этого же пакета (или файла исходного кода, как в OP). Подобным образом, protected означает видимость для подклассов.

C#: В CLR используется пять уровней доступа к членам класса, которые в C# обозначаются как private, protected, public, internal, protected internal. Первые три аналогичны соответствующим модификаторам в C++, Object Pascal и Java. Модификатор internal ограничивает видимость текущей сборкой (т. е. исполняемым файлом или файлом динамической библиотеки, к которому относится данный класс), protected internal разрешат также доступ в унаследованных классах из других сборок.

Файлы, модули и пакеты [ править ]

Свойство: Важное различие между тремя языками заключается в организации исходного кода в файлах. Все три языка используют файлы в качестве стандартного механизма для запоминания исходного кода классов (в отличие от других ОО языков, таких как Smalltalk), но компилятор C++, в отличие от OP или Java, не понимает файлов. Эти же два языка работают с идеей модулей, хотя называют их по-разному.

C++: В C++ программист обычно помещает определение класса в файл объявлений, а определение методов — в отдельный файл кода. Обычно у этих двух файлов одинаковые имена и различные расширения. Компилируемый блок, как правило, ссылается (включает в себя) на свой файл объявлений и на файлы объявлений тех классов (или функций), на которые ссылается код. Все эти соглашения не утруждают компилятор. Это значит, что линкеру предстоит большая работа, потому что компилятор не может знать, в каком другом модуле может быть определен нужный метод. Однако также существуют пространства имен, позволяющие во многих случаях избежать неоднозначностей. Например, классы и прочие элементы библиотеки STL находятся в пространстве имен std.

OP: В Object Pascal каждый файл исходного кода называется unit, и он делится на две части: интерфейс и реализация, отмечаемые соответственно ключевыми словами interface и implementation. Секция интерфейса включает в себя определения классов (с объявлениями методов), а секция реализации должна включать в себя определения методов, объявленных в интерфейсе. Писать фактический код в секции интерфейса нельзя. Вы можете сослаться на объявления другого файла, используя предложение uses. Этим включается в компиляцию интерфейс того файла:

Java: В Java каждый файл исходного кода или единица компиляции компилируется отдельно. Затем вы можете отметить группу единиц компиляции как части одного пакета. В отличие от двух других языков, вы пишете весь код методов тут же при объявлении класса. При включении какого-либо файла предложением import, компилятор читает только public объявления, а не весь код:

C#: На платформе CLR (.NET) код разделен на сборки, которые могут представлять собой динамически подключаемую библиотеку либо исполняемый файл. Кроме того, для группировки типов с целью сведения к минимуму вероятности конфликта имен, существуют пространства имен, которые, однако, никак не связаны со сборками или файлами исходного кода.

Примечание: Модули как пространство имён. Другим важным отличием Java и OP является их способность читать откомпилированные файлы и извлекать из них определения, как бы извлекая заголовки из скомпилированного кода. С другой стороны, для преодоления отсутствия модулей C++ включает пространство имен (namespace). В Java и OP, когда два имени конфликтуют, вы можете просто использовать имя модуля в качестве префикса. Это не требует дополнительной работы по определению пространств имен, а просто включено в языки.

Методы/данные класса и объекта класса [ править ]

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

C++: В C++ методы и данные класса отмечаются ключевым словом static. Данные класса должны быть проинициализированы специальным объявлением, ещё одной уступкой отсутствию модулей.

OP: В OP допустимы только методы класса, которые отмечаются словом class. Данные класса можно заменить, так сказать, приватными глобальными переменными в секции исполнения юнита, описывающего класс.

Java: Java использует то же слово, что и C++, static. Статические методы используются очень часто (и даже слишком) из-за отсутствия глобальных функций. Статические данные можно инициализировать прямо в объявлении класса.

C#: Также, как и в C++ и Java, используется ключевое слово static. Кроме того, можно отметить как статический целый класс, что запрещает создание его объектов и определение нестатических членов.

Классы и наследование [ править ]

Свойство: Наследование у классов — одно из оснований ООП. Оно может быть использовано для выражения генерализации или специализации. Основная идея в том, что вы определяете новый тип, расширяя или модифицируя существующий, другими словами, производный класс обладает всеми данными и методами базового класса, новыми данными и методами и, возможно, модифицирует некоторые из существующих методов. Различные ОО языки используют различные жаргоны для описания этого механизма (derivation, inheritance, sub-classing), для класса, от которого вы наследуете (базовый класс, родительский класс, суперкласс) и для нового класса (производный класс, дочерний класс, подкласс).

C++: C++ использует слова public, protected, и private для определения типа наследования и чтобы спрятать наследуемые методы или данные, делая их приватными или защищёнными. Хотя публичное наследование наиболее часто используется, по умолчанию берётся приватное. Как мы увидим далее, C++ — единственный из этих четырех языков, поддерживающий множественное наследование. Вот пример синтаксиса наследования:

OP: Object Pascal при наследовании использует не ключевые слова, а специальный синтаксис, добавляя в скобках имя базового класса. Этот язык поддерживает только один тип наследования, который в C++ называется публичным. Как мы увидим позднее, классы OP происходят от одного общего базового класса.

Java: Java использует слово extends для выражения единственного типа наследования, соответствующего публичному наследованию в C++. Java не поддерживает множественное наследование. Классы Java тоже происходят от общего базового класса.

C#: C#, как и Java, не поддерживает множественное наследование. Синтаксис наследования несколько отличается от остальных рассматриваемых языков:

Примечание: Конструкторы и инициализация базового класса. В C++, C# и Java у конструкторов наследующих классов сложная структура. В Object Pascal за инициализацию базового объекта отвечает программист. Это довольно сложный раздел, поэтому я пропустил его в этой статье. Вместо этого я сосредоточусь на общем базовом классе, множественном наследовании, интерфейсах, позднем связывании и других родственных предметах.

Предок всех классов [ править ]

Свойство: В некоторых ОО языках каждый класс происходит по крайней мере от некоторого базового класса по умолчанию. Этот класс, часто называемый Object, или подобно этому, обладает некоторыми основными способностями, доступными всем классам. Фактически, все другие классы в обязательном порядке его наследуют. Этот подход является общим ещё и потому, что так первоначально делалось в Smalltalk.

C++: Хотя язык C++ и не поддерживает такое свойство, многие структуры приложений базируются на нём, вводя идею общего базового класса. Пример тому — MFC с его классом COobject. Фактически это имело большой смысл вначале, когда языку не хватало шаблонов.

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

Java: Как и в OP, все классы безоговорочно наследуют класс Object. И в этом языке у общего класса тоже есть некоторые ограниченные свойства и небольшая поддержка RTTI.

C# (CLR): Также имеется класс System.Object (в C# также существует ключевое слово object для его обозначения), являющийся предком не только всех классов, но и любых других типов, даже примитивных, которые также включены в структуру наследования и имеют методы.

Доступ к методам базового класса [ править ]

Свойство: Когда вы пишете метод класса или перекрываете метод базового класса, вам нередко надо сослаться на методы базового класса. Если этот метод переопределен в производном классе, то, используя его имя, вы получите новую версию. В ОО языках есть некоторые приёмы или ключевые слова, позволяющие решить эту проблему.

Илон Маск рекомендует:  Определение ip адреса по домену

C++: В C++ для указания нужного класса можно использовать оператор (::). Вы можете получить доступ не только к методам базового класса, но к классам выше по иерархии. Это очень мощная техника, но она создаёт проблемы, когда вы добавляете в иерархию промежуточный класс.

OP: В Object Pascal для этой цели есть специальное слово inherited. После этого слова вы можете написать имя метода базового класса или (в некоторых случаях) просто использовать это ключевое слово для доступа к соответствующему методу базового класса.

Java: Java для этого использует ключевое слово super. В этом языке, так же, как и в OP, нет возможности сослаться на другой предшествующий класс. На первый взгляд, это может показаться ограничением, но оно позволяет расширять иерархию, вводя промежуточные классы. К тому же, если вы не нуждаетесь в функциях базового класса, вам, наверное, не следует его наследовать.

Совместимость подтипов [ править ]

Свойство: Как я указывал в начале, не все ОО языки строго типизированы, но все три языка, на которых мы сконцентрировались, обладают этим свойством. В основном это означает, что объекты различных классов несовместимы по типу. Из этого правила есть исключение: объекты производных классов совместимы с типом их базового класса. (Примечание: обратное обычно неверно.)

C++: В C++ правило совместимости подтипов справедливо только для указателей и ссылок, но не для обычных объектов. Фактически, у различных объектов разный размер, поэтому их нельзя расположить на том же месте в памяти.

OP: Благодаря ссылочно-объектной модели, совместимость подтипов возможна для каждого объекта. Более того, все объекты совместимы по типу с TObject.

Java: Java использует ту же модель, что и Object Pascal.

Примечание: Полиморфизм. Совместимость подтипов особенно важна для позднего связывания и полиморфизма, как это показано в следующей секции.

Позднее связывание (и полиморфизм) [ править ]

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

C++: В C++ позднее связывание доступно только для виртуальных методов (вызов которых становится немного медленнее). Метод, объявленный в базовом классе как виртуальный (virtual), поддерживает это свойство (но только если описания методов совпадают). Обычные, не виртуальные методы не позволяют позднее связывание, как и OP.

OP: В Object Pascal позднее связывание вводится с помощью ключевых слов virtual и dynamic (разница между ними только в оптимизации). В производных классах переопределённые методы должны быть отмечены словом override (это заставляет компилятор проверять описание метода). Рациональное объяснение этой особенности OP состоит в том, что разрешается больше изменений в базовом классе и предоставляет некоторый дополнительный контроль во время компиляции.

Java: В Java все методы используют позднее связывание, если вы не отметите их явно как final. Финальные методы не могут быть переопределены и вызываются быстрее. В Java написание методов с нужной сигнатурой жизненно важно для обеспечения полиморфизма. Тот факт, что в Java по умолчанию используется позднее связывание, тогда как в C++ стандартом является раннее связывание, — явный признак разного подхода этих двух языков: C++ временами жертвует ОО моделью в пользу эффективности, тогда как Java — наоборот

Примечание: Позднее связывание для конструкторов и деструкторов. Object Pascal, в отличие от других двух языков, позволяет определять виртуальные конструкторы. Все три языка поддерживают виртуальные деструкторы.

Абстрактные методы и классы [ править ]

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

C++: В C++ абстрактные методы или чисто виртуальные функции получаются добавлением так называемого чистого описателя (=0) в определение метода. Абстрактные классы являются просто классами с одним или более абстрактным методом (или наследующие их). Вы не можете создать объект абстрактного класса.

OP: Object Pascal для выделения этих методов использует ключевое слово abstract. Кроме того, абстрактными классами являются классы, имеющие или наследующие абстрактные методы. Вы можете создать объект абстрактного класса (хотя компилятор выдаст предупреждающее сообщение). Это подвергает программу риску вызвать абстрактный метод, что приведёт к генерации ошибки времени выполнения и завершению программы.

Java: В Java и абстрактные методы, и абстрактные классы отмечаются ключевым словом abstract (действительно, в Java обязательно определять как абстрактный, класс, имеющий абстрактные методы, — хотя это кажется некоторым излишеством). Производные классы, которые не переопределяют все абстрактные методы, должны быть отмечены как абстрактные. Как и в C++, нельзя создавать объекты абстрактных классов.

Множественное наследование и интерфейсы [ править ]

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

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

Java: Java, как и Object Pascal, не поддерживает множественное наследование, но полностью поддерживает интерфейсы. Методы интерфейсов допускают полиморфизм, и Вы можете использовать объект, осуществляющий интерфейс, когда ожидается интерфейсный объект. Класс может наследовать или расширить всего лишь один базовый класс, но может осуществить (это — ключевое слово) многочисленные интерфейсы. Хотя это не было спланировано заранее, интерфейсы Java очень хорошо укладываются в модель COM. Вот пример интерфейса:

OP: Delphi 3 ввел в Object Pascal понятие, подобное интерфейсам Java, но эти интерфейсы строго соответствуют COM (хотя технически возможно использовать их в обычных не-COM программах). Интерфейсы формируют иерархию, отдельную от классов, но, как и в Java, класс может наследовать одному базовому классу и осуществлять различные интерфейсы. Отображение методов класса на методы интерфейсов, осуществляемых классом, является одной из наиболее сложных частей языка Object Pascal. Delphi 4 добавляет к этой структуре возможность передать реализацию интерфейса подобъекту, делая эту технику почти такой же эффективной, как и множественное наследование.

Другие свойства [ править ]

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

RTTI [ править ]

Свойство: В строго типизованных ОО языках компилятор осуществляет весь контроль типов, так что нет особой необходимости хранить информацию о классах и типах в работающей программе. Тем не менее, есть случаи (как, например, динамическое преобразование типов), которые требуют информацию о типе. По этой причине все три ОО языка, рассматриваемые здесь, более или менее поддерживают Идентификацию/Информацию о Типе Времени Выполнения (RTTI).

C++: первоначально не поддерживал RTTI. Это было добавлено позже для динамического преобразования типа (dynamic_cast) и сделало доступной некоторую информацию о типе для классов. Вы можете запросить идентификацию типа для объекта, и проверить, принадлежат ли два объекта одному классу.

OP: поддерживает и требует много RTTI. Доступен не только контроль соответствия и динамическое преобразование типов (с помощью операторов is и as). Классы генерируют расширенную RTTI для своих published свойств, методов и полей. Фактически это ключевое слово управляет частью генерации RTTI. Вся идея свойств, механизм потоков (файлы форм — DFM), и среда Delphi, начиная с Инспектора Объектов, сильно опирается на RTTI классов. У класса TObject есть (кроме прочих) методы ClassName и ClassType. ClassType возвращает переменную типа класса, объект специального типа ссылки на класс (который не является самим классом).

Java: как и в Object Pascal, в Java тоже есть единый базовый класс, помогающий следить за информацией о классе. Безопасное преобразование типов (type-safe downcast) встроено в этот язык. Метод getClass() возвращает своего рода метакласс (объект класса, описывающего классы), и Вы можете применить функцию getName() для того, чтобы получить строку с именем класса. Вы можете также использовать оператор instanceof. Java включает в себя расширенную RTTI для классов или интроспекцию, которая была введена для поддержки компонентной модели JavaBeans. В Java существует возможность создавать классы во время исполнения программы.

Пример: Вот синтаксис безопасного преобразования типов на всех трех языках. В случае ошибки в Delphi и Java происходит исключение, а в С++ возвращается нулевой указатель:

Обработка исключений [ править ]

Свойство: Основная идея обработки исключений — упростить код обработки ошибок в программе, предоставив стандартный встроенный механизм, с целью сделать программы более устойчивыми. Обработка исключений — это тема, требующая отдельного рассмотрения, поэтому я только очерчу некоторые ключевые элементы и различия.

C++: C++ использует ключевое слово throw для генерации исключения, try для отметки охраняемого блока и catch для записи кода обработки исключения. Исключения — объекты специального класса, которые могут образовывать некоторую иерархию во всех трёх языках. При возникновении исключения C++ выполняет очистку стека до точки перехвата исключения. Перед удалением каждого объекта в стеке вызывается соответствующий деструктор.

OP: Object Pascal использует подобные ключевые слова: raise, try, и except и обладает подобными свойствами. Единственное существенное отличие состоит в том, что опустошение стека не производится, просто потому, что в стеке нет объектов. Кроме того, вы можете добавить в конце блока try слово finally, отмечая блок, который должен выполняться всегда, независимо от того, было или нет вызвано исключение. В Delphi классы исключений — производные Exception.

Java: Использует ключевые слова C++, но ведёт себя как Object Pascal, включая дополнительное ключевое слово finally. (Это общее свойство всех языков со ссылочно-объектной моделью, оно включено Borland также и в C++Builder 3.) Присутствие алгоритма сборки мусора ограничивает использование finally в классе, который распределяет другие ресурсы, кроме памяти. Также Java строже требует, чтобы все функции, которые могут вызвать исключение, описывали в соответствующем блоке, какие исключения могут быть вызваны функцией. Эти описания исключений проверяются компилятором, что является хорошим свойством, даже если оно подразумевает некоторую дополнительную работу для программиста. В классах Java объекты-исключения должны наследовать класс Throwable.

Шаблоны (обобщенное программирование) [ править ]

Свойство: Обобщенное программирование — это техника написания функций и классов, оставляя некоторые типы данных неопределёнными. Спецификация типа осуществляется, когда эта функция или класс используется в исходном коде. Всё делается под строгим контролем компилятора, и ничего не остаётся без определения во время выполнения. Наиболее типичный пример шаблона класса — это контейнерные классы.

C++: есть шаблонные классы и функции, отмечаемые ключевым словом template. Стандартный C++ включает обширную библиотеку шаблонов, называемую STL (Standart Template Library ,Стандартная библиотека шаблонов), которая поддерживает специфический и мощный стиль программирования: обобщенное программирование. C++ — единственный из рассматриваемых трех языков, который основывается на поддержке обобщенного программирования, помимо ООП.

OP: нет шаблонов. Контейнерные классы обычно строятся как контейнеры объектов класса TObject, а затем уточняются для необходимых объектов.

Java: реализуются в рамках Generics (введенного в JDK 1.5 «Tiger»). Концептуально они не отличаются от шаблонов в C++, но имеют некоторые особенности, которые диктуются свойствами самого языка. В отличие от C++, в Java невозможно во время выполнения получить информацию о конкретном типе шаблона. Предусмотрены контейнеры на все случаи жизни: List (хранение последовательностей элементов), Map или ассоциативные массивы (связывание одних объектов с другими), Set (уникальность значений для каждого типа).

Другие специфические свойства [ править ]

Свойство: Есть еще другие свойства, не упомянутые мной, хотя они важны, только из-за того, что они специфичны только для одного из трёх языков.

C++: Я уже упомянул множественное наследование, виртуальные базовые классы и шаблоны. Эти свойства отсутствуют в двух других ОО языках. В C++ есть ещё перегрузка операторов, тогда как перегрузка методов присутствует также в Java и была недавно добавлена в Object Pascal. C++ позволяет программистам перегружать и глобальные функции. Вы можете перегрузить операторы преобразования типов, написав конвертирующие методы, которые будут вызываться «за кулисами». Объектная модель C++ требует копировать конструкторы и перегружать операторы присваивания, в чем не нуждаются остальные два языка, поскольку базируются на ссылочно-объектной модели.

Java: Только Java поддерживает многопоточность непосредственно в языке. Объекты и методы поддерживают механизм синхронизации (с ключевым словом synchronized): два синхронизированных метода одного класса не могут выполняться одновременно. Для создания нового потока вы просто наследуете от класса Thread, перегружая метод run(). Как альтернативу вы можете осуществить интерфейс Runnable (что вы обычно делаете в апплетах, поддерживающих многопоточность). Мы уже обсуждали сборщик мусора. Ещё одно ключевое свойство Java, конечно, идея переносимого байтового кода, но это не относится непосредственно к языку. Другое примечательное свойство — это поддержка основанных на языке компонентов, известных как JavaBeans и многие другие свойства, недавно добавленные в этот язык.

OP: Вот некоторые специфические черты Object Pascal: ссылки на классы, легкие для использования указатели на методы (основа модели обработки событий) и, в частности, свойства (property). Свойство — это просто имя, скрывающее путь, которым вы получаете доступ к данным или методу. Свойство может проецироваться на прямое чтение или запись данных, а может ссылаться на метод, обеспечивающий доступ. Даже если вы меняете способ доступа к данным, вам не нужно менять вызывающий код (хотя вам нужно будет его перекомпилировать): это делает свойства очень мощным средством инкапсуляции.

Стандарты [ править ]

Свойство: Для каждого языка требуется, чтобы кто-то установил его стандарт и проверял все реализации на соответствие ему.

C++: Стандарт ANSI/ISO C++ явился завершением многотрудных усилий соответствующего комитета. Большинство авторов компиляторов, кажется, пытаются подчиняться стандарту, хотя есть ещё много странностей. Теоретически развитие языка должно на этом закончиться. На практике, инициативы вроде компилятора Borland C++Builder, конечно, не способствуют улучшению ситуации, но многие чувствуют, что C++ очень нуждается в визуальном окружении программирования. В то же время, популярный Visual C++ тянет C++ в другом направлении, например, с явным злоупотреблением макросов. (По моему личному мнению, у каждого языка есть собственная модель развития, и поэтому нет большого смысла в попытках использовать язык для того, для чего он не был предназначен.) Много новых возможностей будут введены новым стандартом C++ 0x.

OP: Object Pascal — язык-собственность, поэтому у него нет стандарта. Borland лицензировал язык для пары продавцов небольших компиляторов на OS/2, но это не оказало большого влияния. Borland расширяет язык с каждым новым выпуском Delphi.

Java: Компания-создатель Sun обладает торговой маркой Java. Однако Sun лицензирует его для продавцов других компиляторов, и убедило ISO создать стандарт Java, не создавая специальный комитет, а просто приняв предложения Sun как есть. Кроме формального стандарта, однако, Java требует высокосовместимых JVM. С недавней поры Sun выдвинула инициативу открыть исходные коды Java (OpenJDK) и сделать ее доступной для всех разработчиков в рамках лицензии GPL 2.

Заключение: Языки и программное окружение [ править ]

Как я упоминал в начале, хотя я пытался исследовать эти языки, только сравнивая синтаксические и семантические характеристики, важно рассмотреть их в соответствующем контексте. Языки нацелены на различные потребности, что означает, что они решают разные проблемы разными способами и используются в очень разных средах программирования. Хотя как языки, так и их среда копируют характеристики друг друга, они были сконструированы для разных потребностей, и в этом вы можете убедиться, сравнивая их характеристики.

Цель C++ — мощность и контроль за счет сложности. Целью Delphi является легкое, визуальное программирование (не отказываясь от мощности) и прочная связь с Windows. Цель Java — мобильность, даже за счет некоторого отказа от скорости, и распределённые приложения или исполняемое содержание WWW (хотя это, конечно, — не Microsoft-овский взгляд на Java!).

Можно определить, что успех этих трех языков зависит не от технических характеристик, которые я включил в эту статью. Финансовый статус Borland, операционная система управления Microsoft, популярность Sun в мире Internet, тот факт, что Java рассматривается как anti-Microsoft-овский язык, будущее браузеров Паутины и Win32 API, роль и признание модели ActiveX (из-за связанной с ней проблемой безопасности) и три уровня архитектуры Delphi — вот показатели, которые могли повлиять на ваш выбор сильнее, чем технические элементы. Например, такой хороший язык как Eiffel, у которого Object Pascal и Java взяли не только некоторое вдохновение, никогда не получит реальной доли рынка, хотя он был популярен во многих университетах земного шара.

Просто имейте в виду, что «модный» становится все более частым словом в компьютерном мире. Как пользователи хотят иметь инструменты этого года (вероятно, по этой причине операционные системы называются по тому году, в котором они выпущены), программисты любят работать с последним языком программирования и первыми овладеть им. Можно наверняка утверждать, что Java — не последний из языков ООП. Через несколько следующих лет найдется кто-то с новым модным языком, и все прыгнут в этот поезд, думая, что нельзя отставать, и забывая, что большинство программистов в мире всё ещё печатают на клавиатуре на добром старом Cobol! (С последним утверждением можно поспорить.)

Python. Урок 14. Классы и объекты

Данный урок посвящен объектно-ориентированному программированию в Python. Разобраны такие темы как создание объектов и классов, работа с конструктором, наследование и полиморфизм в Python.

Основные понятия объектно-ориентированного программирования

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

Выделяют три основных “столпа” ООП- это инкапсуляция, наследование и полиморфизм.

Инкапсуляция

Под инкапсуляцией понимается сокрытие деталей реализации, данных и т.п. от внешней стороны . Например, можно определить класс “холодильник”, который будет содержать следующие данные: производитель, объем, количество камер хранения, потребляемая мощность и т.п., и методы: открыть/закрыть холодильник, включить/выключить, но при этом реализация того, как происходит непосредственно включение и выключение пользователю вашего класса не доступна, что позволяет ее менять без опасения, что это может отразиться на использующе й класс «холодильник» программе. При этом класс становится новым типом данных в рамках разрабатываемой программы. Можно создавать переменные этого нового типа, такие переменные называются объекты.

Наследование

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

Примером базового класса, демонстрирующего наследование, можно определить класс “автомобиль”, имеющий атрибуты: масса, мощность двигателя, объем топливного бака и методы: завести и заглушить. У такого класса может быть потомок – “грузовой автомобиль”, он будет содержать те же атрибуты и методы, что и класс “автомобиль”, и дополнительные свойства: количество осей, мощность компрессора и т.п..

Полиморфизм

Полиморфизм позволяет одинаково обращаться с объектами, имеющими однотипный интерфейс, независимо от внутренней реализации объекта. Например, с объектом класса “грузовой автомобиль” можно производить те же операции, что и с объектом класса “автомобиль”, т.к. первый является наследником второго, при этом обратное утверждение неверно (во всяком случае не всегда). Другими словами полиморфизм предполагает разную реализацию методов с одинаковыми именами. Это очень полезно при наследовании, когда в классе наследнике можно переопределить методы класса родителя.

Классы в Python

Создание классов и объектов

Создание класса в Python начинается с инструкции class. Вот так будет выглядеть минимальный класс.

Класс состоит из объявления (инструкция class), имени класса (нашем случае это имя C) и тела класса, которое содержит атрибуты и методы (в нашем минимальном классе есть только одна инструкция pass).

Для того чтобы создать объект класса необходимо воспользоваться следующим синтаксисом:

имя_объекта = имя_класса()

Статические и динамические атрибуты класса

Как уже было сказано выше, класс может содержать атрибуты и методы. Атрибут может быть статическим и динамическим (уровня объекта класса). Суть в том, что для работы со статическим атрибутом, вам не нужно создавать экземпляр класса, а для работы с динамическим – нужно. Пример:

В представленном выше классе, атрибут default_color – это статический атрибут, и доступ к нему, как было сказано выше, можно получить не создавая объект класса Rectangle.

width и height – это динамические атрибуты, при их создании было использовано ключевое слово self. Пока просто примите это как должное, более подробно про self будет рассказано ниже. Для доступа к width и height предварительно нужно создать объект класса Rectangle:

Если обратиться через класс, то получим ошибку:

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

Проверим ещё раз значение атрибута default_color:

Присвоим ему новое значение:

Создадим два объекта класса Rectangle и проверим, что default_color у них совпадает:

Если поменять значение default_color через имя класса Rectangle, то все будет ожидаемо: у объектов r1 и r2 это значение изменится, но если поменять его через экземпляр класса, то у экземпляра будет создан атрибут с таким же именем как статический, а доступ к последнему будет потерян:

Меняем default_color через r1:

При этом у r2 остается значение статического атрибута:

Вообще напрямую работать с атрибутами – не очень хорошая идея, лучше для этого использовать свойства.

Методы класса

Добавим к нашему классу метод. Метод – это функция, находящаяся внутри класса и выполняющая определенную работу.

Методы бывают статическими, классовыми (среднее между статическими и обычными) и уровня класса (будем их называть просто словом метод). Статический метод создается с декоратором @staticmethod, классовый – с декоратором @classmethod, первым аргументом в него передается cls, обычный метод создается без специального декоратора, ему первым аргументом передается self:

Статический и классовый метод можно вызвать, не создавая экземпляр класса, для вызова ex_method() нужен объект:

Конструктор класса и инициализация экземпляра класса

В Python разделяют конструктор класса и метод для инициализации экземпляра класса. Конструктор класса это метод __new__(cls, *args, **kwargs) для инициализации экземпляра класса используется метод __init__(self). При этом, как вы могли заметить __new__ – это классовый метод, а __init__ таким не является. Метод __new__ редко переопределяется, чаще используется реализация от базового класса object (см. раздел Наследование), __init__ же наоборот является очень удобным способом задать параметры объекта при его создании.

Создадим реализацию класса Rectangle с измененным конструктором и инициализатором, через который задается ширина и высота прямоугольника:

Что такое self?

До этого момента вы уже успели познакомиться с ключевым словом self. self – это ссылка на текущий экземпляр класса, в таких языках как Java, C# аналогом является ключевое слово this. Через self вы получаете доступ к атрибутам и методам класса внутри него:

В приведенной реализации метод area получает доступ к атрибутам width и height для расчета площади. Если бы в качестве первого параметра не было указано self, то при попытке вызвать area программа была бы остановлена с ошибкой.

Уровни доступа атрибута и метода

Если вы знакомы с языками программирования Java, C#, C++ то, наверное, уже задались вопросом: “а как управлять уровнем доступа?”. В перечисленных языка вы можете явно указать для переменной, что доступ к ней снаружи класса запрещен, это делается с помощью ключевых слов (private, protected и т.д.). В Python таких возможностей нет, и любой может обратиться к атрибутам и методам вашего класса, если возникнет такая необходимость. Это существенный недостаток этого языка, т.к. нарушается один из ключевых принципов ООП – инкапсуляция. Хорошим тоном считается, что для чтения/изменения какого-то атрибута должны использоваться специальные методы, которые называются getter/setter, их можно реализовать, но ничего не помешает изменить атрибут напрямую. При этом есть соглашение, что метод или атрибут, который начинается с нижнего подчеркивания, является скрытым, и снаружи класса трогать его не нужно (хотя сделать это можно).

Внесем соответствующие изменения в класс Rectangle:

В приведенном примере для доступа к _width и _height используются специальные методы, но ничего не мешает вам обратиться к ним (атрибутам) напрямую.

Если же атрибут или метод начинается с двух подчеркиваний, то тут напрямую вы к нему уже не обратитесь (простым образом). Модифицируем наш класс Rectangle:

Попытка обратиться к __width напрямую вызовет ошибку, нужно работать только через get_width():

Но на самом деле это сделать можно, просто этот атрибут теперь для внешнего использования носит название: _Rectangle__width:

Свойства

Свойством называется такой метод класса, работа с которым подобна работе с атрибутом. Для объявления метода свойством необходимо использовать декоратор @property.

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

Сделаем реализацию класса Rectangle с использованием свойств:

Теперь работать с width и height можно так, как будто они являются атрибутами:

Можно не только читать, но и задавать новые значения свойствам:

Если вы обратили внимание: в setter’ах этих свойств осуществляется проверка входных значений, если значение меньше нуля, то будет выброшено исключение ValueError:

Наследование

В организации наследования участвуют как минимум два класса: класс родитель и класс потомок. При этом возможно множественное наследование, в этом случае у класса потомка может быть несколько родителей. Не все языки программирования поддерживают множественное наследование, но в Python можно его использовать. По умолчанию все классы в Python являются наследниками от object, явно этот факт указывать не нужно.

Синтаксически создание класса с указанием его родителя выглядит так:

class имя_класса(имя_родителя1, [имя_родителя2,…, имя_родителя_n])

Переработаем наш пример так, чтобы в нем присутствовало наследование:

Родительским классом является Figure, который при инициализации принимает цвет фигуры и предоставляет его через свойства. Rectangle – класс наследник от Figure. Обратите внимание на его метод __init__: в нем первым делом вызывается конструктор (хотя это не совсем верно, но будем говорить так) его родительского класса:

super – это ключевое слово, которое используется для обращения к родительскому классу.

Теперь у объекта класса Rectangle помимо уже знакомых свойств width и height появилось свойство color:

Полиморфизм

Как уже было сказано во введении в рамках ООП полиморфизм, как правило, используется с позиции переопределения методов базового класса в классе наследнике. Проще всего это рассмотреть на примере. Добавим в наш базовый класс метод info(), который печатает сводную информацию по объекту класса Figure и переопределим этот метод в классе Rectangle, добавим в него дополнительные данные:

Посмотрим, как это работает

Таким образом, класс наследник может расширять функционал класса родителя.

P.S.

Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. На нашем сайте вы можете найти вводные уроки по этой теме. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.

Python. Урок 14. Классы и объекты : 12 комментариев

А вот если Вы добавите вот это
.entry-title a:last-child <
float:right;
>
в свой css будет намного удобнее, нежели вы будите использовать 2-ную табуляцию в HTML. Спасибо.

Класс, о методе super() вообще ни слова

Спасибо за замечание! Добавим!

Про self ничего не сказано. Похоже на ссылку на текущий обьект.

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

О методе __new__(cls) тоже нет ни слова, а он так же участвует в конструировании экземпляра класса.

ОК, спасибо! Добавим!

Наконец-то всё стало понятно. Огромное спасибо за разъяснение на уровне 1 класса 2 четверти!

Определение инкапсуляции неверное. Приведенное определение скорее присуще самому понятию “класс”. А инкапсуляция – это сокрытие деталей реализации.

> Атрибут может быть статическим и не статическим (уровня объекта класса)

В других языках принято “не статические атрибуты” называть динамическими. Предлагаю использовать, чтобы язык не ломать ��

Введение в Scala.

С нуля до распределенных приложений.

Классы и объекты

15 June 2015 от Tagir

Scala гибридный функциональный/ОО язык и обладает одним из самых мощных аппаратов по работе с классами и типами. Создатель языка, Мартин Одерски — автор generics в языке Java.

Синтаксис создания классов:

Тело класса является основным конструктором. Все поля и функции объявленные в нем являются членами объектов класса.

Дополнительные конструкторы объявляются функциями с названием this и должны вызывать основной конструктор:

Модификаторы доступа

В Scala доступны обычные модификаторы доступа для полей класса:

  • public, доступно всем клиентам класса
  • protected, доступно в классе и всем производным
  • private, доступно только объектам данного класса

Кроме того, модификаторы могут быть расширены квалификаторами [this] или [X]. Первый ограничивает доступ до одного объекта, второй делает поле видимым всем включающим классам или пакетам до X.

Модификаторы по умолчанию

Все поля классов по умолчанию имеют модификатор public.

Сделано это тех соображений, что Scala ориентирована на использование неизменяемых значений, поэтому “утечка” поля класса в интерфейс не дает возможности сломать программу, а только облегчает тестирование.

Параметры конструкторов имеют по умолчанию модификатор private[this]. Для превращения их в поле класса надо добавить в объявление ключевое слово val/var.

x — имеет модификатор public, y — имеет модификатор private[this]

Особый синтаксис

Несколько операций в Scala вызывают методы с определенным названием в классе. Оператор == вызванный на объекте класса вызывает метод equals, а ( ) метод apply.

Объекты-синглтоны

В Scala нет статических (static) методов, но есть особый синтаксис для создания объектов-синглтонов.

Этот объект будет создан в единственном экземпляре и доступен в любое время. Их можно использовать как объекты некоторого класса, для этого достаточно указать, что они унаследованы от него ключевым словом extends.

Объекты-компаньоны

Для каждого класса можно объявить специальный объект-компаньон (companion object). Это объект объявленный в том же файле и с тем же именем, что и класс. Этот объект имеет доступ к private полям класса.

Возможно вы уже встречались с созданием некоторых объектов без использования ключевого слова new. Здесь нет магии, это вызов метода apply на объекте-компаньоне.

Часто дополнительные конструкторы класса объявляют именно так.

5. Java — Классы и объекты

Java является объектно-ориентированным языком программирования. Как язык, который имеет функцию объектно-ориентирования, он поддерживает следующие основные понятия:

  • полиморфизм;
  • наследование;
  • инкапсуляция;
  • абстракция;
  • классы;
  • объекты;
  • экземпляр;
  • метод;
  • парсинг.

В этом уроке мы рассмотрим объекты и классы в Java, их концепции.

Класс может быть определен как шаблон (обозначен зеленым цветом), который описывает поведение объекта, который в свою очередь имеет состояние и поведение. Он является экземпляром класса. Например: собака может иметь состояние — цвет, имя, а также и поведение — кивать, лаять, есть.

Содержание

Объекты в Java

Давайте теперь посмотрим вглубь, что является объектами. Если мы рассмотрим реальный мир, то найдём много предметов вокруг нас, автомобили, собаки, люди, и т.д. Все они имеют состояние и образ жизни.

Если учесть, собаку, то ее состояние — имя, порода, цвет, а образ жизни — лай, виляние хвостом, бег.

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

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

Классы в Java

Класс, из которого создаются отдельные объекты, обозначен зеленым цветом.

Пример создания класса в Java, приводится ниже:

Класс может содержать любой из следующих видов переменных:

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

В Java классы могут иметь любое количество методов для доступа к значению различных видов методов. В приведенном выше примере, barking(), hungry() и sleeping() являются методами.

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

Конструктор класса

При обсуждении вопроса класса, одной из наиболее важных подтем в Java является конструктор. Каждый класс имеет конструктор. Если мы не напишем его или, например, забудем, компилятор создаст его по умолчанию для этого класса.

Каждый раз, когда в Java создается новый объект, будет вызываться по меньшей мере один конструктор. Главное правило является то, что они должны иметь то же имя, что и класс, который может иметь более одного конструктора.

Пример конструктора приведен ниже:

Примечание: в следующих разделах мы будем более подробно обсуждать, если у нас будет два разных типа конструкторов.

Создание объекта

Варианты как создать объект в классе следующие:

  • Объявление: объявление переменной с именем переменной с типом объекта.
  • Инстанцирование: используется «новое» ключевое слово.
  • Инициализация: «новое» ключевое слово сопровождается вызовом конструктора. Этот вызов инициализирует новый объект.

Пример приводится ниже:

Если Вы скомпилируете и запустите выше программу, то она выдаст следующий результат:

Доступ к переменным экземпляра и методам в Java

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

Пример

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

Если Вы скомпилируете и запустите выше программу, то она выдаст следующий результат:

Правила объявления классов, операторов импорта и пакетов в исходном файле

В последней части этого раздела давайте рассмотрим правила декларации исходного файла. Эти правила в Java имеют важное значение при объявлении классов, операторов импорта и операторов пакета в исходном файле.

  • В исходном файле может быть только один публичный класс (public class).
  • Исходный файл может иметь несколько «непубличных» классов.
  • Название публичного класса должно совпадать с именем исходного файла, который должен иметь расширение .java в конце. Например: имя класса public class Employee<>, то исходный файл должен быть Employee.java.
  • Если класс определен внутри пакета, то оператор пакет должно быть первым оператором в исходном файле.
  • Если присутствуют операторы импорта, то они должны быть написаны между операторами пакета и объявлением класса. Если нет никаких операторов пакета, то оператор импорта должен быть первой строкой в исходном файле.
  • Операторы импорта и пакета будут одинаково выполняться для всех классов, присутствующих в исходном файле. В Java не представляется возможным объявить различные операторы импорта и/или пакета к различным классам в исходном файле.

Классы имеют несколько уровней доступа и существуют различные типы классов: абстрактные классы (abstract class), конечные классы (final class) и т.д. Обо всем этом обсудим в уроке модификаторы доступа.

Помимо указанных выше типов классов, Java также имеет некоторые специальные классы, называемые внутренние (Inner class) и анонимные классы (Anonymous class).

Java пакет (package)

При разработке приложений сотни классов и интерфейсов будет написано, поэтому категоризации этих классов является обязательным, а также это делает жизнь намного проще.

Операторы импорта (import)

Если задать полное имя, которое включает в себя пакет и имя класса, то компилятор может легко найти исходный код или классы. В Java импорт это способ задать правильное место для компилятора, чтобы найти конкретный класс.

Например, следующая строка будет просить компилятор загрузить все классы, доступные в каталоге «java_installation/java/io»:

Простой пример по выше описанному

Для нашего обучения создадим два класса. Это будут классы Employee и EmployeeTest.

Для начала откройте блокнот и добавьте следующий код. Помните, что этот класс Employee является открытым или публичным классом. Теперь сохраните исходный файл с именем Employee.java.

Класс Employee имеет четыре переменных экземпляра name, age, designation и salary. Он имеет один явно определенный конструктор, который принимает параметр.

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

Ниже приводится класс EmployeeTest, в котором создаются два экземпляра класса Employee и вызывают методы для каждого объекта, для присвоения значений каждой переменной.

Сохраните следующий код в файл «EmployeeTest.java»:

Теперь, скомпилировав оба класса, запустим EmployeeTest и получим следующий результат:

В следующем уроке обсудим основные типы данных, и как они могут быть использованы при разработке java-приложений.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL