Что такое объект c
Перейти к содержимому

Что такое объект c

  • автор:

Что такое объект c

Доброго времени суток! Настало время поговорить о таких понятиях как класс и объект в контексте объектно-ориентированного программирования и языка C#. На самом деле, мы уже не раз сталкивались с классами и объектами, тот же метод «Main» (в котором мы пишем практически весь код) принадлежит классу «Program».

Можно сказать, что класс – это описание какого-то типа данных. Это некий абстрактный шаблон (набор правил), по которому мы можем создавать объекты (переменные, того типа данных, который описывает класс). Объект – это созданный экземпляр класса. Например, есть класс «Студент», который характеризует среднестатистического студента (с фамилией, именем, возрастом и т.п.), а есть конкретный студент Иванов Иван 20-ти лет. Вот в данном случае, Иванов Иван 20-ти лет как раз и является экземпляром класса! Т.е. конкретно существующей сущностью. Или другой пример, форма для выпечки хлеба является своего рода «классом», а выпеченные в ней булки хлеба – экземплярами. А теперь разберемся как создавать классы.

Хотя, на практике, Вам чаще придется работать с объектами классов, который создали не Вы (например, с объектами стандартных классов)…

И так, вот самый простой пример создания класса:

//Класс, описывающий простого студента class Student < //Тело класса >

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

Student firstStudent = new Student();

Объекты классов, создаются почти так же, как и переменные стандартных типов, только после знака «=» мы используем ключевое слово new и дальше снова повторяем имя класса с круглыми скобками после него.

Очень похоже на вызов метода. Позже Вы поймете почему так!

Но на практике от такого «студента» толка будет мало, так как наш класс пустой… Так что, давайте заполним тело класса полезным содержимым! В этом уроке мы рассмотрим, как «заполнять» тело класса полями и методами.

Сначала разберемся с полями класса. Создаются они практически так же, как мы создавали переменные в методах, только есть одно отличие, перед полем класса желательно указать уровень доступа к нему. А указывается он с помощью одного из ключевых слов (модификаторов доступа), таких как public (доступ открыт) и private (доступ закрыт).

В C# есть еще и другие модификаторы доступа, но на данном этапе они нам пока не нужны.

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

//Класс, описывающий простого студента class Student < //Тело класса public string name; //Имя студента public uint grade; //Курс >

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

//Создаем объект-студента (пока "безликого") Student ivanStudent = new Student(); //Задаем значения полей объекта ivanStudent.name = "Иван"; //Задаем имя ivanStudent.grade = 2; //Задаем курс

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

Обратите внимание как мы получаем доступ к полям объекта, сначала мы указываем имя объекта, а затем, через точку ­– имя поля. Обращение к методам и свойствам осуществляется так же.

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

Но на практике так лучше не делать. Как правило, поля классов делают закрытыми, чтобы пользователь класса не мог работать с ними на прямую. Потому, что каждый объект – это сложная система, которая «живет» по своим правилам, и некорректное вмешательство из вне, может привести к нарушению целостности!

Если бы мы объявили поля класса закрытыми, т.е. вместо слова public написали слово private, то подобного рода код, просто не компилировался:

//Задаем значения полей объекта ivanStudent.name = "Иван"; //Задаем имя ivanStudent.grade = 2; //Задаем курс

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

Внутри методов класса (как открытых, так и закрытых), можно получить доступ к любому, даже самому закрытому полю класса!

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

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

//Класс, описывающий простого студента class Student < //Возвращает значение поля name public string GetName() < return name; >//Возвращает значение поля grade public uint GetGrade() < return grade; >//Устанавливает значение поля name public void SetName(string aName) < name = aName; >//Устанавливает значение поля grade public void SetGrade(uint aGrade) < grade = aGrade; >//Тело класса private string name; //Имя студента private uint grade; //Курс >

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

//Создаем объект-студента (пока "безликого") Student ivanStudent = new Student(); //Задаем значения полей объекта ivanStudent.SetName("Иван"); //Задаем имя ivanStudent.SetGrade(2); //Задаем курс

На самом деле, в нашем случае, методы, устанавливающие значения полям, не особо отличаются функциональностью, но на практике, часто такие методы могут быть оснащены дополнительными проверками аргументов, например, методы могли бы не устанавливать слишком длинные имена, или курс старше шестого…

На сегодня, наверное, хватит информации. В этом уроке, мы обсудили понятия класса, объекта, полей и методов, а в следующем уроке мы поговорим про свойства в классах.

Реализация объектов в C

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

В самой vtable есть один указатель на каждый метод в каждом интерфейсе, поддерживаемом объектом . Порядок указателей должен соответствовать порядку методов в спецификации интерфейса, опубликованной в файле заголовка Mapidefs.h. Для каждого указателя функции в vtable задается адрес фактической реализации метода. В C++ компилятор автоматически настраивает vtable. В C это не так.

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

Реализация объекта

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

typedef struct _MYSTATUSOBJECT < const STATUS_Vtbl FAR *lpVtbl; ULONG cRef; ANOTHEROBJ *pObj; LPMAPIPROP lpProp; LPFREEBUFFER lpFreeBuf; >MYSTATUSOBJECT, *LPMYSTATUSOBJ; 

Так как этот объект является объектом состояния, vtable включает указатели на реализации каждого из методов в интерфейсе IMAPIStatus : IMAPIProp , а также указатели на реализации каждого из методов в базовых интерфейсах — IUnknown и IMAPIProp. Порядок методов в vtable соответствует указанному порядку, определенному в файле заголовка Mapidefs.h.

static const MYOBJECT_Vtbl vtblSTATUS = < STATUS_QueryInterface, STATUS_AddRef, STATUS_Release, STATUS_GetLastError, STATUS_SaveChanges, STATUS_GetProps, STATUS_GetPropList, STATUS_OpenProperty, STATUS_SetProps, STATUS_DeleteProps, STATUS_CopyTo, STATUS_CopyProps, STATUS_GetNamesFromIDs, STATUS_GetIDsFromNames, STATUS_ValidateState, STATUS_SettingsDialog, STATUS_ChangePassword, STATUS_FlushQueues >; 

Клиенты и поставщики услуг, написанные на языке C, используют объекты косвенно через vtable и добавляют указатель объекта в качестве первого параметра в каждом вызове. Для каждого вызова метода интерфейса MAPI требуется указатель на объект, вызываемый в качестве первого параметра. Для этой цели C++ определяет специальный указатель, известный как этот указатель. Компилятор C++ неявно добавляет этот указатель в качестве первого параметра для каждого вызова метода. В C нет такого указателя; он должен быть добавлен явным образом.

В следующем коде показано, как клиент может выполнить вызов экземпляра MYSTATUSOBJECT:

lpMyObj->lpVtbl->ValidateState(lpMyObj, ulUIParam, ulFlags); 

См. также

Классы, структуры и пространства имен

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

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

В принципе ранее уже использовались классы. Например, тип string , который представляет строку, фактически является классом. Или, например, класс Console , у которого метод WriteLine() выводит на консоль некоторую информацию. Теперь же посмотрим, как мы можем определять свои собственные классы.

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

class название_класса < // содержимое класса >

После слова class идет имя класса и далее в фигурных скобках идет собственно содержимое класса. Например, определим в файле Program.cs класс Person, который будет представлять человека:

class Person

Классы и объекты в языке программирования C# и .NET

Начиная с версии C# 12, если класс имеет пустое определение, то фигурные скобки после названия типа можно не использовать:

class Person;

Однако такой класс не особо показателен, поэтому добавим в него некоторую функциональность.

Поля и методы класса

Класс может хранить некоторые данные. Для хранения данных в классе применяются поля . По сути поля класса — это переменные, определенные на уровне класса.

Кроме того, класс может определять некоторое поведение или выполняемые действия. Для определения поведения в классе применяются методы.

Итак, добавим в класс Person поля и методы:

class Person < public string name = "Undefined"; // имя public int age; // возраст public void Print() < Console.WriteLine($"Имя: Возраст: "); > >

В данном случае в классе Person определено поле name , которое хранит имя, и поле age , которое хранит возраст человека. В отличие от переменных, определенных в методах, поля класса могут иметь модификаторы, которые указываются перед полем. Так, в данном случае, чтобы все поля были доступны вне класса Person поля определены с модификатором public .

При определении полей мы можем присвоить им некоторые значения, как в примере выше в случае переменной name . Если поля класса не инициализированы, то они получают значения по умолчанию. Для переменных числовых типов это число 0.

Также в классе Person определен метод Print() . Методы класса имеют доступ к его поля, и в данном случае обращаемся к полям класса name и age для вывода их значения на консоль. И чтобы этот метод был виден вне класса, он также определен с модификатором public .

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

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

new конструктор_класса(параметры_конструктора);

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

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

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

Теперь создадим объект класса Person:

Person tom = new Person(); // создание объекта класса Person // определение класса Person class Person < public string name = "Undefined"; public int age; public void Print() < Console.WriteLine($"Имя: Возраст: "); > >

создание классов в языке программирования C# и .NET

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

Обращение к функциональности класса

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

объект.поле_класса объект.метод_класса(параметры_метода)

Например, обратимся к полям и методам объекта Person:

Person tom = new Person(); // создание объекта класса Person // Получаем значение полей в переменные string personName = tom.name; int personAge = tom.age; Console.WriteLine($"Имя: Возраст "); // Имя: Undefined Возраст: 0 // устанавливаем новые значения полей tom.name = "Tom"; tom.age = 37; // обращаемся к методу Print tom.Print(); // Имя: Tom Возраст: 37 class Person < public string name = "Undefined"; public int age; public void Print() < Console.WriteLine($"Имя: Возраст: "); > >

Консольный вывод данной программы:

Имя: Undefined Возраст: 0 Имя: Tom Возраст: 37

Добавление класса

Обычно классы помещаются в отдельные файлы. Нередко для одного класса предназначен один файл. Если мы работаем над проектом вне среды Visual Studio, используя .NET CLI, то нам достаточно добавить новый файл класса в папку проекта. Например, добавим новый файл, который назовем Person.cs и в котором определим следующий код:

class Person < public string name = "Undefined"; public void Print() < Console.WriteLine($"Person "); > >

Здесь определен класс Person с одним полем name и методом Print.

В файле Program.cs , который представляет основной файл программы используем класс Person:

Person tom = new Person(); tom.name = "Tom"; tom.Print(); // Person Tom

Использование классов в проекте в Visual Studio в языке программирования C#

Visual Studio предоставляет по умолчанию встроенные шаблоны для добвления класса. Для добавления класса нажмем в Visual Studio правой кнопкой мыши на название проекта:

Добавление класса в Visual Studio в C#

В появившемся контекстном меню выберем пункт Add -> New Item. (или Add -> Class. )

В открывшемся окне добавления нового элемента убедимся, что в центральной части с шаблонами элементов у нас выбран пункт Class . А внизу окна в поле Name введем название добавляемого класса — пусть он будет назваться Person :

Добавление нового класса в Visual Studio в C#

В качестве названия класса можно вводить как Person, так и Person.cs. И после нажатия на кнопку добавления в проект будет добавлен новый класс, в котором можно определить тот же код и также использовать в файле Program.cs.

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

Объекты — создание экземпляров типов

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

Поведение статических типов отличается от описанного здесь поведения. Дополнительные сведения см. в статье Статические классы и члены статических классов.

Экземпляры структуры и Экземпляры классов

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

Экземпляры классов создаются с помощью new оператора . В приведенном ниже примере Person является типом, а person1 и person2 — экземплярами или объектами этого типа.

using System; public class Person < public string Name < get; set; >public int Age < get; set; >public Person(string name, int age) < Name = name; Age = age; >// Other properties, methods, events. > class Program < static void Main() < Person person1 = new Person("Leopold", 6); Console.WriteLine("person1 Name = Age = ", person1.Name, person1.Age); // Declare new person, assign person1 to it. Person person2 = person1; // Change the name of person2, and person1 also changes. person2.Name = "Molly"; person2.Age = 16; Console.WriteLine("person2 Name = Age = ", person2.Name, person2.Age); Console.WriteLine("person1 Name = Age = ", person1.Name, person1.Age); > > /* Output: person1 Name = Leopold Age = 6 person2 Name = Molly Age = 16 person1 Name = Molly Age = 16 */ 

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

using System; namespace Example < public struct Person < public string Name; public int Age; public Person(string name, int age) < Name = name; Age = age; >> public class Application < static void Main() < // Create struct instance and initialize by using "new". // Memory is allocated on thread stack. Person p1 = new Person("Alex", 9); Console.WriteLine("p1 Name = Age = ", p1.Name, p1.Age); // Create new struct object. Note that struct can be initialized // without using "new". Person p2 = p1; // Assign values to p2 members. p2.Name = "Spencer"; p2.Age = 7; Console.WriteLine("p2 Name = Age = ", p2.Name, p2.Age); // p1 values remain unchanged because p2 is copy. Console.WriteLine("p1 Name = Age = ", p1.Name, p1.Age); > > /* Output: p1 Name = Alex Age = 9 p2 Name = Spencer Age = 7 p1 Name = Alex Age = 9 */ > 

Память для p1 и p2 выделена в стеке потока. Эта память освобождается вместе с типом или методом, в котором она объявлена. Эта одна из причин того, почему структуры копируются при присваивании. Напротив, при выходе всех ссылок на объект из области действия среда CLR автоматически освобождает память (выполняет сборку мусора), выделенную для экземпляра класса. Детерминированное уничтожение объекта класса, как в C++, невозможно. Дополнительные сведения о сборке мусора в .NET см. в статье Сборка мусора.

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

Идентификация объектов и равенство значений

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

  • Чтобы определить, ссылаются ли два экземпляра класса на одно расположение в памяти (то есть имеют одинаковый идентификатор), воспользуйтесь статическим методом Object.Equals. (System.Object является неявным базовым классом для всех типов значений и ссылочных типов, включая структуры и классы, определенные пользователем.)
  • Чтобы определить, имеют ли поля экземпляра в двух экземплярах структуры одинаковые значения, воспользуйтесь методом ValueType.Equals. Так как все структуры неявно наследуются от System.ValueType, метод можно вызвать непосредственно в объекте, как показано в следующем примере:

// Person is defined in the previous example. //public struct Person // < // public string Name; // public int Age; // public Person(string name, int age) // < // Name = name; // Age = age; // >//> Person p1 = new Person("Wallace", 75); Person p2 = new Person("", 42); p2.Name = "Wallace"; p2.Age = 75; if (p2.Equals(p1)) Console.WriteLine("p2 and p1 have the same values."); // Output: p2 and p1 have the same values. 

Связанные разделы

Совместная работа с нами на GitHub

Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *