Про курс
Відео курс "С# Базовий" (С# Essential) представлений 18-ма взаємопов'язаними відео уроками, які дозволять Вам повністю розібратися з синтаксисом мови C# та її семантикою, а також опанувати об'єктно-орієнтоване програмування (ООП) мовою С#, зрозуміти подійно-орієнтований, структурний, функціональний та аспектно-орієнтований підходи.
Вивчення відео курсу "С# Базовий" починається з основних понять та парадигм ООП (об'єктно-орієнтованого програмування мовою C#). Буде розглянуто поняття класів та особливості мови C#. Закінчується курс розглядом базових понять технологій LINQ та архітектури .NET Framework.
Відео курс дасть Вам необхідний рівень знань та навичок для вивчення більш складних технологій, якими має володіти .NET Developer.
Цей курс входить до спеціальності:
Попередні Вимоги
Володіння мовою C# на рівні курсу “C# Стартовий”.
Ви навчитеся
- розуміти основні принципи побудови та структурування додатків, написаних мовою програмування C#;
- реалізовувати принципи ООП – наслідування, інкапсуляція, поліморфізм, абстракція – у програмах на C#;
- використовувати класи, інтерфейси;
- використовувати перелічення (enum), універсальні шаблони (generics), делегати;
- працювати з подіями, обробляти винятки, керувати потоками;
- застосовувати технологію LINQ для роботи з даними.
- 31 год 26 хв
- 03.07.2013
- 18
- 26.01.2018
- російська
Що входить до курсу
×
Ви дійсно бажаєте відкрити доступ до тестування за курсом С# Базовий (ООП) на 40 днів?
Перший відео урок містить масу корисної інформації: автор протягом 3-х годин докладно та максимально зрозуміло розповідає про класи, їх поля, властивості та екземпляри, а також розкриває тему об'єктно-орієнтованого програмування та демонструє реалізацію одного з її головних принципів – інкапсуляцію. Весь матеріал підкріплюється зрозумілими прикладами.
Вивчивши зміст цього уроку, ви зможете:
- розуміти суть основних парадигм ООП;
- розуміти, як влаштовані та для чого призначені класи;
- створювати класи та правильно їх використовувати;
- розуміти призначення полів та властивостей, вміти їх реалізовувати;
- створювати конструктор класу;
- розуміти принцип інкапсуляції та реалізовувати його мовою C#.
У відео уроці "Класи та об'єкти. Діаграми класів" буде продовжено тему уроку "Введення в OOП. Класи та об'єкти", а також буде розкрито тему можливості мови програмування C# розділяти визначення класу між двома та/або більше файлами, що називається частковими або partial класами. Після ознайомлення з частковими класами в С# будуть розглянуті діаграми класів, зв'язки відношень між класами, такі як асоціація, агрегація, композиція, реалізація, самоасоціація залежності та інші.
Всі мови ООП, включаючи С#, засновані на трьох парадигмах (концепціях), які називають інкапсуляцією, наслідуванням і поліморфізмом. В ході відео уроку Вам буде представлена інформація про дві основні парадигми ООП – поліморфізм і наслідування, також Ви познайомитеся з модифікаторами доступу та віртуальними членами. В ході уроку, на прикладах, представлених тренером, Ви зможете зрозуміти практичне застосування поліморфізму та наслідування та навчитеся працювати з ієрархією класів.
У відео уроці буде продемонстровано практичні приклади створення та використання абстрактних класів та інтерфейсів. Абстракція дозволяє програмісту розглядати об'єкт, не розбираючи суму складних частин, з яких складається даний об'єкт. Під час відео уроку Ви отримаєте необхідні знання, які допоможуть Вам розібратися з поняттям абстракції в С#, а також навчитеся створювати та реалізовувати власні абстрактні класи та інтерфейси і розуміти різницю між ними.
У відео уроці розглянуто приклади створення і практичного застосування масивів та індексаторів. Тренер пояснює Вам принципи створення і практичного застосування індексаторів та способи їх перевизначення. Програмісти з досвідом знайомі із процесом звернення до індивідуальних елементів, що містять стандартні масиви. У відео уроці буде представлена можливість мови програмування С# проєктувати спеціальні класи, які можна індексувати подібно до стандартного масиву через визначення індексатора. Індексатори часто використовують при створенні спеціальних типів – колекцій. У цьому уроці Ви докладно вивчите можливості індексаторів у C#.
У відео уроці будуть розглянуті статичні класи, принципи створення та практичного застосування статичних членів. Також у ході відео уроку буде пояснено роботу та використання розширювальних методів. У другій частині відео уроку тренер розгляне поняття вкладених класів та шаблон проєктування "Одиночка" (Singleton).
У відео уроці буде представлена повна інформація про структури, розглянуто відмінності між класами та структурами, а також розказано практичні поради щодо їх застосування. Структури – фундаментальні типи даних мови програмування C#. Структури за своєю суттю прості і часто програмісти-початківці можуть не розуміти, наскільки швидко робота зі структурами може стати складною.
У цьому відео уроці будуть розглянуті такі поняття, як пакування (boxing) та розпакування (unboxing), структурний тип DateTime, а також робота з переліченнями (enum). Під час заняття тренер познайомить студентів із практичними прикладами, які дозволять з легкістю використовувати та застосовувати отримані на уроці знання. Останньою темою, розглянутою у відео уроці будуть перелічення, які надають спосіб ефективно визначити набір іменованих інтегральних констант.
Відео урок дозволяє студенту зрозуміти роботу лямбда-виразів та делегатів. Програмісти-початківці, продумуючи інтерфейс чергового класу, часто розуміють, що дуже корисною могла б бути можливість передавати у якості аргументу методів частину виконуваного коду. Делегати дозволяють Вам реалізувати такий підхід. У відео уроці буде представлена інформація про створення та застосування делегатів, а також основні труднощі під час роботи з ними.
У мові програмування C# існує два механізми для створення коду, який буде повторно використаний через різні типи – вже розглянуте раніше наслідування та узагальнення. Узагальнення, на відміну від наслідування, виражають повторне використання коду через використання універсальних шаблонів (generics), у яких застосовуються різні типи даних на етапі виконання. Під час відео уроку тренер розгляне з Вами всі основи роботи з узагальненнями та їх застосування у мові програмування C#, а також розповість про контрваріантність та коваріантність.
У відео уроці "Обмеження універсальних шаблонів" на Вас чекає продовження знайомства з універсальними шаблонами в C#. Ви дізнаєтесь, як можна використовувати обмеження для узагальнених типів даних. Під час відео уроку тренер зупинить Вашу увагу на роботі з Nullable типами, а також на операціях поглинання, показавши приклади їх практичного використання.
Весь відео урок буде повністю присвячений роботі з подіями в C#. У деталях буде розглянуто, яким чином створювати "видавців" та "підписників", а також звертатися до створених подій та викликати їх. Тренер приділить окрему увагу делегату EventHandler та базовому класу EventArgs, а також роботі з ними.
У процесі перегляду відео уроку Ви отримаєте основні відомості, які будуть потрібні Вам для роботи з багатопоточністю в мові програмування C#. Багатопотоковість – важливий засіб багатозадачного програмування середовища .NET. Відео урок дасть Вам основне розуміння багатопоточності у мові програмування С#. Також під час уроку тренер розповість Вам про використання делегатів ThreadStart і ParameterizedThreadStart і пояснить роботу з критичними секціями, як засобами синхронізації доступу потоків до різних ресурсів, котрі розділяються.
У відео уроці будуть пояснені колекції, їх призначення та приклади їхнього практичного застосування. Також Ви детально вивчите базові інтерфейси IEnumerable, IEnumerator. Також в ході відео уроку тренер розгляне з Вами приклади створення та використання колекцій користувача, продемонструє наочні приклади по роботі оператора yield.
У цьому відео уроці Ви дізнаєтеся, які системні винятки існують в мові C# і як правильно обробляти виняткові ситуації за допомогою конструкції try-catch-finally. Також ви навчитеся створювати свої об'єкти виключення. Під час виконання програми може скластися ситуація, коли коректне виконання програми неможливе. Наприклад, програма читає файл на диску, якого немає. У такій ситуації у додатку виникає спеціальний об'єкт – виняток. Виняток описує проблему, яка виникла в момент виконання коду і дозволяє розробнику обрати потрібну дію для вирішення проблеми.
У даному відео уроці тренером буде розглянуто базовий клас object, його застосування та використання, а також техніка перевантаження операторів. У процесі пояснення буде пройдено техніку клонування, а також буде розглянуто призначення шаблону проєктування «Прототип» (Prototype) та інтерфейсу ICloneable. Вам буде продемонстровано практичне використання техніки перевантаження операторів.
Даний відео урок буде дуже важливим для розуміння всіх сучасних технологій. Під час відео уроку тренер познайомить Вас з основами LINQ, а також з анонімними та динамічними типами, що активно використовуються при побудові запитів. Мова LINQ – це набір функцій, що дозволяє значно розширити можливості синтаксису мови програмування C#. Language Integrated Query (LINQ) представляє розширення мови C#, яке дає можливість працювати з різними джерелами даних, використовуючи синтаксис запитів, подібний до мови SQL.
У цьому відеоуроці Ви дізнаєтеся, що таке простір імен і як правильно організовувати проєкт, використовуючи простір імен. Також Ви дізнаєтесь, як створювати бібліотеки (DLL) у мові C#. Тренер розгляне тип проекту Class Library та на простому прикладі пояснить, для чого використовуються бібліотеки. Наприкінці уроку Ви вивчите нові модифікатори доступу internal і internal protected та розглянете деякі препроцесорні директиви, дізнаєтесь, як вони можуть допомогти при розробці великих рішень.
Здравствуйте!
Тема нашего сегодняшнего урока “Классы и объекты”. На протяжении урока, мы с Вами рассмотрим такие понятия как:
ООП(Объектно-ориентированное программирование); подробно рассмотрим, что же это за подход, на чем базируется этот подход;
Также разберем такие важные составляющие объектно-ориентированных языков, это: классы и объекты. Мы научимся создавать как классы, так и объекты этих классов. Также, мы разберем с Вами составляющие самих классов, то есть, те конструкции, которые могут входить в состав класса. Еще мы познакомимся с одной интересной конструкцией, которая не присутствует в большинстве объектно-ориентированных языков, но в C# она есть, это свойства. Также, мы разберем на протяжении урока, еще много всего интересного и рассмотрим много примеров.
Поэтому давайте приступим к нашему уроку.
И первое с чего мы начнем, это рассмотрение такого понятия, как объектно-ориентированное программирование. Сразу хотелось бы сказать, что в мире существует много различных подходов к программированию. Соответственно, в зависимости от этих подходов, и формируются компьютерные языки. Компьютерные языки программирования. Существуют низкоуровневые подходы, на таких подходах формируется Ассемблер. Ассемблер, это язык, который, по сути, содержит мнемоники для машинных команд. Мнемоника, это человеческое название, каждой отдельной машинной команды, которая может слегка конфигурировать. Мы не будем рассматривать такие подходы. Они, конечно же, не используются, ни в процедурных языках, ни в объектно-ориентированных. Процедурные языки, это следующий этап развития компьютерных языков. Процедурные языки, в себя включают такие простейшие конструкции, как переменные, условные конструкции, циклические конструкции, методы, некоторые языки включают в себя даже структуру - это некие блоки, которые позволяют группировать в себе переменные, например сходные по какому-то назначению. Объектно-ориентированные языки расширяются уже новыми синтаксическими конструкциями, которые позволяют описать объекты из объективной реальности. Давайте мы посмотрим сейчас, маленький пример, чтобы понять, как же строится объектно ориентированное программирование, а потом мы вернемся к нашему методичному изложению.
Представьте себе, что Вы пошли в лес собирать коллекцию насекомых. Обратите внимание, какое множество насекомых. Мы берем сачок и собираем все подряд: ловим мух, жуков, и всех кто попадется под руку. Давайте посмотрим, что, в объективной реальности, представляют собой эти насекомые. Вы скажете – “В общем, это отдельные объекты, которые мы собираем, для какой-то коллекции насекомых, для какого-то института”. И вот представьте, что мы насобирали много-много насекомых, сгребли их в одну кучу, пришли в свой биологический институт и вывалили их на стол из мешка. Подходит наш профессор и говорит:” Ребята, теперь вам нужно рассортировать их, потому что, вы здесь, в одну кучу, бабочек, кузнечиков, пауков, муравьёв, стрекоз и всех. Так не пойдет. Это просто какая-то куча объектов”. Значит, нам нужно рассортировать их по некому типу. Вот мы садимся и начинаем их рассортировывать или еще можно сказать по другому – классифицировать. В отдельную кучу мы отбираем всех стрекоз, в отдельную кучку – всех бабочек, в отдельную кучу – всех пауков. То есть, мы сейчас разложили их по классам насекомых. Есть класс “Стрекозы”(или тип или вид), класс “Бабочки” и класс “Пауки”. Обратите внимание – мы все объекты классифицировали, по какому то признаку. Ведь, почему называется класс “Бабочки”? Наверное, потому что, у них большие крылья. А почему, класс “Пауки” так называется? Наверное, потому что у них столько то лапок и они плетут паутину. Это, к примеру, мы не будем углубляться в биологические особенности этих насекомых. Посмотрите, в результате нашей сортировки, у нас получились некие абстрактные классы. Допустим здесь у нас смешаны разные виды бабочек – и махаоны, и капустница, и другие разные особи. Подходит снова наш профессор и говорит:” Теперь отберите в одну сторону махаонов, а в другую сторону - капустниц”. Следующее, что мы делаем – берем и разбираем бабочек на две кучки. В одну кучку мы складываем всех капустниц, а в другую – все разновидности махаонов. Обратите внимание – все насекомые, это общий класс. Далее мы их классифицируем на стрекозы, бабочки и пауки. А что такое бабочка? Что такое паук? Что такое стрекоза? Это что-то абстрактное, что-то общее. Бабочка, это собирательное понятие от насекомых с определенными признаками. Это абстракция. Абстрактный класс насекомых. Далее переходя к двум нашим группам бабочек, задаемся вопросом – а что же тогда это? Кто-то скажет, что перед нами абстрактный класс капустниц, например. У них тоже есть много разновидностей. В другой кучке, у нас, разные махаоны. Разные классы махаонов. Это тоже некие абстрактные классы. Потому что конкретный класс, это конкретный махаон. А все вместе они составляют абстрактный класс бабочек махаонов. В свою очередь, абстрактный класс всех бабочек состоит из абстрактных классов бабочек махаонов и бабочек капустниц.
Теперь, проклассифицировав этих бабочек, мы представим, как бы было хорошо, если бы нашим родным подарить живую бабочку. Что нам нужно делать? Бабочки из коллекции нам не подходят- они уже порядком присушенные. У нас есть два варианта – либо вернуться в лес и ловить эту бабочку, после чего поместить ее в банку, либо мы можем пойти к специальному человеку, который все знает о бабочках и умеет их делать. Мы подходим к этому человеку, заходим к нему в офис, а он нам говорит:” У нас Вы можете купить живую бабочку, при чем мы их не ловим, а создаем сами. У нас есть чертежи каждого насекомого. Исходя из чертежей, мы можем сделать Вам кузнечика, бабочку или стрекозу”. Обратите внимание чертежи, каждого, конкретного насекомого. К примеру, вот чертеж, этого махаона-адмирала, вот этот чертеж – бабочки-капустницы. Также отдельно, есть чертежи на любую бабочку, просто здесь мы их не видим. И как Вы видите, чертеж с детальным устройством каждого отдельного насекомого называется тоже классом, но конкретным классом. В данном примере, если бабочки в наших “кучах” – абстрактные классы насекомых, то отдельный чертеж принято называть отдельным классов. Потому что, Вы никогда не догадаетесь, что он будет делать с этим чертежом, так как он сам, вручную, может сделать эту живую бабочку. Как же это будет происходить? У него стоит такой аппарат “3D-Bioplotter”, который может вырастить гортань, отдельно выбранные органы людям и в данном случае – нашу бабочку. Как же работает этот биоплоттер? Снова же по неким программам. Обратите внимание: человек, который управляет этим биоплоттером берет программу(вот этот чертеж), вставляет в специальный разъем, для чертежей и после того, как эта программа отработает, у нас появляется готовая бабочка, которую мы помещаем в банку. Обратите внимание: мы взяли класс “Чертеж”, вставили его в биоплоттер, и на основании этого класса(чертежа), мы получили готовый живой объект. Еще раз смотрим: у нас имеются классы бабочек(чертежи), имеется машина, которая создает этих бабочек и в итоге, машина выдает нам готовый, живой объект. Запомните, понятие “объект” всегда было первичным. Все ученые сначала собирали информацию о существующих объектах, а потом начинали классифицировать их, то есть группировать по неким классам. Поэтому и программист, который будет разрабатывать какую-то программу и захочет создать свой виртуальный объект(а мы сейчас посмотрим как они создаются), он должен сначала в голове представить живой, работающий объект, а только потом взять и нарисовать на бумаге чертеж этого объекта. Допустим, как мог появиться автомобиль, если человек сначала не представит в движении, в работе? Поэтому человек сначала представляет себе еще несуществующий доселе объект, рисует его на бумаге и только потом этот объект появляется(материализуется) в объективной реальности. В итоге получается, для того, чтобы нам получить какой-то искусственный объект, нам нужен чертеж, по которому этот объект будет сконструирован. Давайте еще раз посмотрим вот эту эволюцию. Пошли в лес, насобирали гору насекомых, далее, профессор нам сказал расклассифицировать их. Первый раз мы их разобрали на три группы – стрекозы, бабочки и пауки. Это абстрактные классы насекомых: абстрактный класс бабочек, абстрактный класс стрекоз и абстрактный класс пауков. Абстрактный класс бабочек у нас разделяется еще на два абстрактных класса: капустницы и махаоны. Каждая отдельная бабочка(махаон или капустница), представляет собой конкретный класс. Далее, мы идем к генетикам и просим их вырастить вот такую бабочку. Генетик берет чертеж(некую программу), которая вставляется в этот биоплоттер, где выращивается наша бабочка и в итоге, через какой то промежуток времени, у нас появляется бабочка, которая никогда не была гусеницей.
Хотелось бы посмотреть, как то же самое можно было бы представить на языке программирования, на C#. Как вот эту картину можно было бы осуществить на языке C#. Представьте себе, у нас есть тоже некий аппарат, который позволяет нам создавать чертежи бабочек. Это Visual Studio. Обратите внимание, что мы здесь делаем. На 9 строке, мы создаем класс Butterfly. Обратите внимание на открывающие и закрывающие операторные скобки на 10 и на 18 строке. То есть получается, что это тело класса Butterfly. И мы видим, что класс, в языке программирования C#, это синтаксическая конструкция, которая состоит из трех частей. Первое, это имя class, второе, само имя этого класса и поскольку это чертеж для бабочек, мы назовем наш класс Butterfly. Если же мы захотим сделать стрекозу, мы назовем класс – DragonFly, паука – Spider, муравья – Ant и так далее. Теперь смотрим, класс – это конструкция языка, которая состоит из имени class(это ключевое слово, которое означает некий стереотип) и конечно же тело этого класса. Обратите внимание, тело класса содержит в себе какие-то другие конструкции. Мы видим, что тело класса содержит здесь переменную name. Если это переменная строкового типа name(ключевое слово public, мы разберем попозже), для нас оно пока обязательно. Еще раз обратите внимание, у нас тут имеется переменная типа string, и имеется обычная функция, то есть метод. Имя метода Fly(), этот метод ничего не принимает, ничего не возвращает, и в теле этого цикла, мы, к примеру, десять раз машем крылышками. Обратите внимание, мы сделали чертеж. Класс, это некий чертеж, этой бабочки. Класс, это конструкция C# которая может хранить в себе, пока мы видим: переменные и функции. Переменные, мы будем называть полями. Если переменная находится в теле класса, мы такие переменные, будем называть полями. То есть считайте, что это синоним. А вот все функции, которые находятся в теле класса, мы будем называть методами. Еще раз: мы создаем класс Butterfly, в теле этого класса, мы создаем строковое поле name, и создаем метод Fly(), который ничего не принимает, ничего не возвращает и в теле этого метода бабочка машет крыльями. А теперь нам хотелось бы создать объект и увидеть эту бабочку. Для того чтобы это сделать, нам нужно выполнить следующий подход. На 24 строке, мы создаем в теле метода Main(), переменную с именем mahaon типа ButterFly. Обратите внимание, мы создали собственный тип. Если раньше, мы работали с простейшими типами, например тип int, булевый, стоковой тип, то теперь, мы научились создавать свои собственные типы. Итак, мы создаем переменную, с именем mahaon, типа ButterFly. Это значит, что эта переменная, сможет содержать в себе бабочку. Далее, используем знак присвоения, после знака присвоения, используем ключевое слово new. Что такое ключевое слово new? Это значит, что мы просим у компьютера, чтобы нам выделили место на куче. Куча, это специальная область памяти, которая может хранить в себе уже готовые объекты. Получается, что на 24 строке, мы создаем некий объект-бабочку, вот таким способом. Мы создаем переменную типа ButterFly, здесь мы используем слово new, которое просит выделить нам место в памяти(в оперативной памяти), для того, чтобы можно было как-то построить эту бабочку в ОЗУ. Когда мы выполним эту программу, у нас сформируется исполняемый файл. Когда мы его запустим, то системный загрузчик, скопирует его содержимое(коды программ которые здесь содержатся) в оперативно запоминающее устройство(ОЗУ). Из чего же оно состоит? Обратите внимание, у нас в памяти появился аналог нашей виртуальной бабочки. Вот это и есть настоящий объект нашего класса ButterFly. Именно на 24 строке, словом new попросили систему выделить набор байт(область), где мы можем разместить коды нашей бабочки, то есть коды, которые мы написали в нашем классе, спроецируются в ОЗУ. То есть, наш класс перекодируется в машинные коды и окажется в ОЗУ, в такой области памяти, которая называется кучей. У нас есть две важные области памяти, которые требуются для работы программы: первая область памяти, это стек и вторая область называется кучей(Heap). Это не новое понятие, это понятие пришло к нам с операционной системы Windows. Так вот на куче, как раз и строятся все объекты.
Далее, мы можем вывести эту бабочку на экран. Если она у нас есть в памяти, то мы можем отобразить ее на экране. Конечно, для того, чтобы она была такая красивая, нам надо еще больше потрудиться над нашим классом, потому что такой класс не нарисует такую бабочку, но тем не менее он показывает саму эволюцию шагов как класс превращается в объект и где этот объект находится. В теле метода Main(), на 24 строке мы как раз и создаем объект класса ButterFly. Этот объект создается на куче. Куча, это область памяти для хранения объектов в памяти.
Итак, сформулируем, что такое класс, а что такое объект. Класс – конструкция языка C#, которая содержит в себе поля и методы. Объект – область памяти в ОЗУ, потому что класс находится в Visual Studio программиста, а объект, это выделенная область памяти, в которой находятся наши поля и методы. Получается, что объект, это сущность времени выполнения программы. Класс – сущность времени программирования, а оказавшись в памяти – называется объектом. Для того чтобы построить такой класс, мы должны выполнить вот такую запись, мы должны создать переменные(в данном случае она будет хранить адрес первого байта, с которого начинается тело нашего объекта.). Далее мы можем обратиться к нашему объекту. Мы берем и махаону даем имя и через точку обращаемся, к нашему объекту. В данном случае, мы даем ему имя “Admiral”. И вот это имя и запишется в это поле name. Далее, мы могли бы написать mahaon.Fly() и наша бабочка запустилась бы и начала бы 10 раз махать крыльями(назовем это так).
Мы рассмотрели простейшее определение вот таких понятий как классы и объекты и в объектно-ориентированном программировании очень важно проводить проекцию между жизненными сущностями, между объектами из объективной реальности и накладывать эту проекцию на объекты виртуальной реальности. Как уже можно увидеть, объектно-ориентированное программирование позволяет нам, программным кодом, описывать какие то жизненные сущности. Посмотрите как просто бабочку! Мы создаем сложную бабочку ,что код нашего класса очень сложный, большой. Здесь очень много сложной математики, которая моделирует поведение этой бабочки. Кап просто создать объект этой бабочки. Смотрите, как просто обращаться к членам класса этой бабочки. Здесь мы обращаемся к полю name и присваиваем ему имя admiral. Дальше мы можем написать mahaon.Fly() и она полетит. Смотрите, как просто управлять этой бабочкой. И заметьте: в одном месте мы создали класс, а в другом месте, мы создаем объект этого класса. И часто бывает так, что одни программисты создают классы, а другие – создают объекты этих классов. При этом Вы, как программист, создавший объект бабочки, должны понимать, что может делать эта бабочка. К примеру, она может летать, она может пить какой-нибудь нектар(к примеру) и поэтому Вам дается только открытый интерфейс взаимодействия с этой бабочкой. Это значит, что Вам дается список тех функций, которые Вы можете использовать. Например, мы можем заставить бабочку полететь, но нам недоступна функция биения сердца. Мы не имеем право ею пользоваться. Посмотрите на сам подход – мы создаем класс, который просто описывает устройства, и создаем объект, то есть во время создания объекта, у нас срабатывает ключевое слово new. И именно это слово запрашивает память у нашей виртуальной машины, которая управляет этой памятью. Запрашивает кучу. Куча подробней будет разбираться на следующих курсах. Пока нам не столь важно понимать устройство кучи, нам важно понимать, что куча – это область памяти, в которой строятся объекты.
Мы переходим с Вами к нашей презентации. И как Вы можете видеть, на этом слайде находится краткое описание объектно-ориентированного программирования. Во первых это парадигма программирования, в которой основными концепциями являются понятия объектов и классов. И как Вы помните из прошлого эпизода, где мы рассматривали шуточный пример с бабочками и с 3D-биоплоттером и с проекцией этой деятельности, по созданию бабочки на виртуальную реальность, на попытку описания создания бабочки в виде программного кода.
Теперь мы продолжим рассматривать такие понятия как объекты и классы в более серьезной манере. Давайте мы перейдем к следующему слайду и посмотрим, что же такое классы.
Класс – это конструкция языка C#, которая может хранить в себе, в данном случае, мы пока видим, поля и методы. Полями мы будем называть переменные, которые находятся в теле класса. Конечно же их можно называть переменными, но так не принято. Мы также знаем, что у нас есть две разновидности переменных, это те переменные, которые располагаются внутри методов и те переменные, которые располагаются в телах классов. Так вот, те переменные, которые располагаются в телах методов, принято называть локальными переменными. Те переменные, которые располагаются в теле класса, как мы видим здесь, принято называть полями. Такие переменные еще называют глобальными переменными, но понятие глобальной переменной, это немножко другое понятие. Вот представьте себе, что мы могли бы писать программы без использования классов. C#, же не позволяет написать даже простейшую программу без использования классов. И на предыдущих курсах, где Вы изучали самые простейшие конструкции, мы все равно находились в теле класса program, поэтому мы не можем создать переменную за пределами класса. А вот понятие глобальной переменной, которое описывается в спецификации с C++, позволяет создать переменную за пределами класса. У нас такого не позволено. В общем, мы видим, что класс, это конструкция языка, которая состоит из трех частей: ключевое слово class, которое определяет тип этой конструкции, имя(идентификатор) этого класса, то есть как мы называем этот класс и блок кода, открывающие/закрывающие операторные скобки и в этом блоке кода(мы его еще будем называть телом класса) находятся поля и методы. Мы больше не будем называть поля переменными, ни глобальными, ни локальными, а уже будем поля называть полями.
Давайте перейдем к следующему слайду и посмотрим, что же такое объекты. Действительно ли все так просто, как мы рассматривали в предыдущем, шуточном, эпизоде. Мы сейчас увидим небольшие отличия от того, что мы просматривали в предыдущем эпизоде а также введем новые термины и уточнения для такого понятия, как объект.
На этом слайде мы видим процесс, создания объекта. Условимся, что у нас уже имеется, в нашей программе, некий класс, MyClass, в теле класса, мы создаем строковую поле field, ничего ему не присваиваем, и создаем метод, который ничего не принимает, ничего не возвращает, и в теле этого метода, выводим на экран значение поля field. Создали класс MyClass, и в нем поле и метод. Теперь мы хотим создать объект этого класса. Но теперь нам нужно перейти к небольшому уточнению. Для того, чтобы нам создать объект, нам нужно выделить место в куче. В операционной системе Windows, есть своя, так называемая куча, это область памяти, где располагается, коды наших программ, которые мы запускаем. Мы понимаем, что условно, коды наших программ, разбиваются, на объекты. Что же нужно для того, чтобы создать объект? Обратите внимание, мы должны сначала создать переменную, в данном случае, мы создаем переменную с именем instance1 и указываем тип этой переменной. Мы теперь понимаем, что эта переменная может хранить в себе, Вы скажете объект, и условно мы можем проассоциировать непосредственно с объектом. Но мы уже понимаем, что объект, это некая область памяти, которая в себе хранит какие-то коды. И мы к этой памяти, каким-то образом будем обращаться. Условимся так – эта переменная будет хранить не сам объект, а как мы уже говорили, а адрес первого байта, с которого начинает располагаться этот объект в памяти. В предыдущем курсе, мы рассматривали, оперативное устройство, видели, что оно состоит из байтов и мы понимаем, что в ОЗУ хранятся как коды программ, так и данные, которые выполняются под выполнением этих кодов. Обратите внимание на ключевое слово new. Ключевое слово new говорит нам о том, что мы хотим выделить область памяти на куче. Операционная система Windows имеет свою кучу, то есть свою область памяти, но мы работаем под управлением виртуальной машины, которая неким образом интерпретирует и компилирует и позволяет выполнять наши программы. Почему компилирует? Потому что, виртуальная машина, ее полное название – интерпретатор компилирующего типа. Это уже из области интерпретаторов/компиляторов. Мы не разбираем такие понятия достаточно глубоко, возможно кто-то их уже разбирал или изучал, если кто-то не изучал – ничего от этого не потеряет. Мы создаем переменную instance1 типа MyClass, это значит, что эта переменная может содержать в себе, некого байта памяти, с которого начинается какой-то объект. И вот такой адрес, в ООП, принято называть ссылкой. И поэтому мы будем говорить, что создаем переменную instance1 типа MyClass, которая будет содержать в себе ссылку на объект типа MyClass. Если Вы попытаетесь сюда подставить другую ссылку, на какую-то другую разновидность объектов. Например, если у нас класс “Бабочка”, то мы никак не сможем подставить сюда первый байт класса “Стрекоза”. Далее знак присвоения, после знака присвоения идет ключевое слово new. Этим мы подтверждаем, что хотим выделить участок памяти на куче. Снова возвращаемся к куче. У операционной системы Windows своя куча, у CLR(это и есть название нашей виртуальной машины) – Common Language Runtime, общеязыковая среда исполнения. Пока что мы представляем, что виртуальная машина, это некая большая программа, которая занимается интерпретацией наших программ. Она берет нашу программу, преобразовывает ее в машинные коды, которые понимает процессор, и подставляет их каким-то образом под процессор и в общем, управляет нашими кодами. И поэтому, у нее имеется своя куча. То есть это выглядит примерно так: наша виртуальная машина CLR говорит операционной системе Windows-“У тебя есть большая куча, я хочу, чтобы когда запускались мои программы, чтобы они не смешивались с твоими, неуправляемыми программами”. то есть программами, которые писались на таких языках, как C++, потому что такие программы будут располагаться на той куче, которая принадлежит операционной системе Windows. Представьте, что куча выглядит ка большая загородка, и CLR говорит:”Выдели мне из своей кучи кусок, я этим куском буду управлять сама”. И вот это ключевое слово new, как раз запрашивает область памяти, которая называется куча. Дело в том, что для каждого приложения, куча строится своя. Когда мы запускаем программу, нам выделяется область памяти, которая называется управляемой кучей. И если у нас запущено несколько программ, которые написаны на языке C#, и работают одновременно, значит и выделено будет несколько таких областей, а каждая область будет принадлежать какому-то отдельному процессу. Процесс, это программа, во время выполнения. Поэтому нельзя думать, что для .Net приложений, куча будет единой – это будет несколько куч. После того, как мы запросили кучу, нам дали область памяти, и далее, обратите внимание, этот метод, похожий на вызов некоего метода. После ключевого слова new идет специальный метод. Обратите внимание на этот код, Вы здесь ничего не видите. Имя этого метода совпадает с именем класса, мы его здесь не видим, но он все равно неявно здесь присутствует. И вот такие вспомогательные методы(специальные методы), называются конструкторами. То есть, после ключевого слова new мы вызываем конструктор – это специальный метод, который и будет заниматься конструированием нашего объекта. Часто говорят, что конструктор строит объект, и это неправда. Задача конструктора одна: если мы видим, что конструктор не принимает никаких параметров, то задача такого конструктора(конструкторы такого плана называются еще конструкторами по умолчанию) проинициализировать все поля класса значениями по умолчанию. Мы уже понимаем, что это поля объекта, потому как класс копируется в память(кучу). Мы знаем, что значение по умолчанию типа string, это null. Еще раз смотрим, что же здесь происходит. На этой строке, мы создаем переменную instance1, типа MyClass, и присваиваем ей ссылку. Что же возвращает эту ссылку? New? Нет, new, только запрашивает кусок памяти, чтобы скопировать туда этот класс, конструктор этого класса строит этот класс – вписывает значения по умолчанию и потом именно конструктор вернет ссылку на вот этот объект. Но теперь небольшая поправка. Посмотрите, что мы делаем ниже. Мы создаем еще одну переменную instance2 типа MyClass тоже и присваиваем ей ссылку на объект. Смотрите, два раза встречается new, два раза отработали конструкторы. Это значит, что в памяти построилось два одинаковых объекта. Представьте себе, что это не MyClass, а бабочка, которую мы рассматривали в предыдущем шуточном эпизоде с бабочками. И получается, что мы построили 2 одинаковых бабочки в памяти. И теперь давайте посмотрим, что же произошло у нас на куче. Обратите внимание, у нас появляются такие понятия, как экземпляр. Что такое экземпляр? Экземпляр – это тоже область памяти, которая неким образом, связана с объектом. Давайте еще раз посмотрим в класс. Обратите внимание, здесь имеется поле field, представьте, что это имя бабочки, а это метод махания крыльями. Дело в том, что у всех этих бабочек одна технология махания крыльями, и поэтому, если мы создадим просто 2 объекта, то придется скопировать вот этот метод, и в экземпляр 1, и в экземпляр2. То есть, если бы у нас использовался такой подход, то у нас в памяти, находилось бы много дублирующегося кода. И если бы у нас было 1000 бабочек, то у нас было бы 1000 одинаковых копий этого метода. Но мы знаем, что оперативно запоминающее устройство и достаточно дорогое, и не резиновое, и именно поэтому, мы стараемся память всегда экономить. Если же память начинает переполняться, компьютер начинает медленнее работать, вследствие чего, мы уже подумаем, пользоваться этой программой или нет. Значит, нам надо сделать так, чтобы у каждого объекта, не было этого метода, и поэтому мы пришли вот к такой топологии. Решили из объекта вынести все переменные в отдельные сущности, и назвать их экземплярами. И теперь, когда мы пытаемся построить объект, у нас сначала строится объект, и в него из нашего класса, копируется только метод. Перед этим объектом имеется специальный блок, который мы пока назовем заголовком, который описывает содержимое того, что находится в объекте. Получается, что этот заголовок тоже начинается с какого-то байта. У экземпляра имеется тоже некий заголовок и в этом заголовке имеется некая ссылочка на этот объект. Посмотрите, что происходит на этой строке. Мы уже не будем говорить, что мы создаем объекты. В данной строке, мы создаем переменную, с именем instance1, типа MyClass и присваиваем ей ссылку на экземпляр класса MyClass. Представьте, что здесь еще пусто, поскольку в нашем классе, поле field было пустым. Что происходит на следующей строке? А здесь, у нас создается еще один экземпляр, но при этом, у нас уже не создается объект. Потому что он уже существует. И когда мы будем обращаться к этим экземплярам(через точку вызывать методы), то мы будем обращаться к экземпляру, а экземпляр уже будет переадресовывать наше обращение по нужному адресу, обращаясь к нужному методу. При этом смотрите, какая экономия памяти, когда мы создаем не объекты, а используем такое понятие, как экземпляры. У нас создается только один объект, куда копируются все методы из класса, а все поля, будут храниться в отдельной области памяти. И получается, что у нас образовались 2 различных сущности: это экземпляры и объекты. То есть, получается, что объект – это область в памяти, управляемой куче, которая хранит в себе методы, а также статические поля, которые мы пока не разбирали. Что же тогда экземпляр? Экземпляр – область памяти, которая хранит в себе нестатические поля, то есть такие, обычные, которые мы создали в нашем классе. Если бы мы написали слово static, это поле стало бы статическим. Мы еще не пишем это слово, потому что мы, пока не знаем, что это и как оно используется. Теперь, когда мы создали два экземпляра(мы уже будем говорить, что мы создаем экземпляры, а то что создается объект, мы уже и так подразумеваем само собой разумеющееся), мы обращаемся к первому экземпляру и записываем в него двойку. Здесь мы обращаемся ко второму экземпляру, потому что второй экземпляр содержит в себе ссылку(адрес) первого байта, блока заголовка. Этот блок, описывает служебную информацию об объекте, а также содержит ссылку на объект. Иначе, как бы мы достучались к объекту, если бы мы вызывали метод. Теперь ниже, мы на экземпляре instance1, вызываем метод. Вы спросите- “а как же происходит вызов метода?” Давайте посмотрим, на примере instance2. Мы вызываем, метод, то есть instance2 содержит ссылку на вот эту область памяти. Но мы знаем, что у нас имеется специальный заголовок у каждого экземпляра. А вот этот заголовок, содержит в себе ссылку на другой заголовок, который принадлежит уже объекту. И теперь, когда мы вызываем метод, мы обращаемся к этому экземпляру, и раз в экземпляре отсутствуют методы, идем в объект, находим нужный метод, выполняем его, и возвращаемся, после его выполнения. А если мы посмотрим, что же делает этот метод, то увидим, что он выводит на экран значение поля field. Поэтому здесь мы сначала построили два экземпляра, при этом неявно построились объекты, а в нашем случае объект строился всего один раз – при построении первого экземпляра. При построении второго экземпляра, объект уже не строился – построился только экземпляр и пересчитались ссылки в заголовках на объект. Посмотрите, как с оптимизирована память. Ведь классы бывают достаточно большие и программный код иногда занимает довольно много места в памяти, и поэтому дублировать их это не позволительная роскошь, поэтому здесь нам приходится использовать некую оптимизацию. Забегая вперед, позначим, что это подход, который описывается паттерном проектирования Flyweight. Pattern – это шаблон объектно-ориентированного проектирования. И получается, что здесь мы проинициализировали эти поля, определенными значениями 2 и 5 и теперь у каждого экземпляра имеется свое поле и свои значения. И на втором экземпляре, мы вызываем метод, который должен вывести значение поля на экран. Это происходит так: сначала, мы ищем метод в экземпляре, и поскольку, экземпляр хранит только статические поля(общие поля, которые можно представить в виде корзины, которой могут пользоваться все экземпляры одновременно), мы переадресуемся в объект. Объект находит в себе нужный метод, и когда начинает выполняться этот метод, то этот метод обращается именно к тому экземпляру, на котором и был вызван этот метод. Как же объект определяет, к какому объекту нужно обращаться? Этим занимаются механизмы, которые занимаются подстановкой адресов. Уже более детально, с пересчетом адресов и язык MSL, мы рассмотрим, на отдельных семинарах, а также Вы можете почитать про это в книге Джеффри Рихтера “CLR via C#”, ее можно увидеть на сайте в описании нашего курса, в разделе рекомендации. Еще раз подводим черту, мы видим, как мы создаем объекты и мы уже понимаем, что объект и экземпляр это две большие разницы. На этой строке, мы создаем переменную instance1 типа MyClass, и присваиваем ей ссылку на экземпляр класса MyClass. Ключевое слово new выделило нам область памяти. MyClass() – конструктор, который в значения полей сначала вписал 0 и только потом, мы переприсвоили значение этой переменной. Далее у нас создается объект, который содержит в себе статические и какие-то общие поля и экземпляр – который содержит нестатические поля. Вот эти статические поля и обеспечивают уникальность вот этого экземпляра. Как мы воспринимаем все? Когда мы говорим объект, мы говорим:”Вот у нас первый объект, который, как бы вбирает в себя эти методы. И он создает иллюзию, как будто у нас все копируется в нашу переменную, но на самом деле она разделяется между разными экземплярами. А вот у нас второй объект, который тоже считает эту функциональность своей и поскольку эта функциональность похожа, она выносится в одно место, в область памяти и для того, чтобы у нас произошло обращение именно к нужному экземпляру, чтобы когда мы вызвали метод, у нас не вывелось значение первого экземпляра, здесь происходит просто пересчет адресов переходов”. Поэтому здесь, эти заголовки и специальные механизмы, которые все это обслуживают, позволяют нам просто смотреть нам на работу объектов и экземпляров. Теперь с этого момента, мы будем проводить различия между объектами и экземплярами. Теперь мы делаем четкое разделение – в .Net, у нас есть экземпляры и объекты. Экземпляром называется область памяти, которая хранит в себе нестатические поля и только нестатические поля, а объект, это разделяемое другими экземплярами. Еще одна маленькая заметка: мы будем часто слышать, что объект обладает двумя природами – состояние и поведение. Состояние объекта определяются значениями его нестатических полей. Вот так, вы видите, что у экземпляра есть нестатическое поле. Поведение (здесь мы приводим понятие “объект”, как обобщающее понятие как “экземпляр” так и “объект”)- определяется программным кодом, то есть методами. Методы мы часто называем поведением, а поля мы называем состоянием. Для того чтобы представить это приведем жизненный пример. Например, идете Вы домой, заходите в подъезд, где Вас встречает консьержка, она смотрит на Вас и думает-“добропорядочный гражданин”. Почему? Потому что, к примеру, Вы всегда здороваетесь, улыбаетесь(ведь улыбка, это некий программный код в Вашей голове, с помощью которого, Вы управляете мышцами), нормально вызываете лифт(не бъете по кнопке), прилично одеты, то есть у Вас соответствующее состояние. То есть Вас сначала оценивают по состоянию. К примеру, создавать кучу переменных: цвет глаз, волосы, одежда и тому подобное. То есть Вас будут описывать с помощью полей. Как же в таком случае будет имитироваться улыбка? Методами - мы будем задавать определенную форму губ. Также будет описываться и приветствие. А теперь представьте, идет другой человек и представьте, что он неформал. С ирокезом, покрашенным в розовый цвет, плюет сквозь зубы, бросает недокуренный бычок в коридоре. Что же скажет консьержка о нем? Она назовет его хулиганом. Она видит его издалека по состоянию- фиолетово-розовый ирокез, косуха, стилы. Она уже узнала, что он неформал, то есть относится к какой-то категории и она уже ожидает увидеть вот такое его поведение(курить в парадном, бросит бутылку из под пива). То есть, если мы захотим создать панка, его волосы, косуху – мы будем описывать переменными, а то, что он плюет, курит – будем описывать методами. И потому – все что объект делает, это его поведение. Все из чего он состоит, как он выглядит – это его состояние. Поэтому эти две природы важно различать – поведение и состояние. Почему? Потому что мы уже в более серьезной литературе, более подробно и детально описывающей объектно-ориентированные парадигмы, объектно-ориентированное проектирование и такие понятия дальше будут встречаться дальше. Поэтому не смущайтесь, когда вы услышите слово “поведение”, которое будет означать методы, а “состояние” – значения полей. Поведение выражается содержимым методов, в состояние – значением полей. А еще мы узнаем, что содержимое методов, должно правильно именоваться, потому что, если мы по другому назовем метод(он бросает бутылку, например), то будет происходить путаница. И важно понимать, что все методы следует именовать глаголами в форме infinity, то есть без частицы to. Это будет правильное именование всех методов класса, а классы, поля и переменные, принято именовать существительными. Например, для того чтобы описать кого то, Вы пишите aleksandr.SaySomething();
Хорошо. Мы с Вами переходим дальше. И давайте перейдем в Visual Studio, и немного поработаем с кодом. Посмотрим, как же у нас в действительности создаются классы и их объекты. Обратите внимание, на 10 строке, мы создаем класс с именем MyClass, в теле класса, на 12 строке, мы создаем строковое поле field. На 14 строке, мы создаем метод с именем Method(), который ничего не принимает и ничего не возвращает. В теле метода, мы выводим на экран значение поля field. Скорее всего, Вас сейчас заинтересовали ключевые слова public. Что же они значат? Эти слова, являются модификаторами доступа к членам класса, то есть к полям, к методам и другим членам, с которыми мы познакомимся попозже. Что это значит? Давайте посмотрим. На 30 строке, мы создаем экземпляр класса MyClass и мы сейчас будем к нему обращаться. Смотрите, на 34 строке, мы полю field, экземпляра instance, присваиваем значение “Hello World!”. Программа нормально работает, красным не подчеркивает. Если мы возьмем и удалим этот модификатор доступа, у нас, как Вы видите, подчеркнет красным, что он не доступен. То есть у него сейчас такой уровень, что мы не сможем к нему обратиться. Зачем так делается, мы разберем немного попозже – буквально через пример-два. Но пока мы не будем особо обращать внимание на модификатор доступа, в данном случае, потому что, на данном этапе, мы пока разбираем момент построения объекта. Создание класса и построение объекта данного класса. Обратите внимание, на 30 строке, мы создаем переменную instance(instance переводится на русский, как экземпляр) типа MyClass, и присваиваем ей ссылку на экземпляр класса MyClass, построенному в куче. Мы все равно понимаем, что вместе с экземпляром, на куче построился объект . Вот, обратите внимание, с 24 по 28 строку, где в комментариях написаны 4 возможных способа комментирования этого кода. Мы часто будем встречать в комментариях правильные нотации, правильного произношения, потому что это очень важно. Вы знаете – кто ясно мыслит, тот ясно излагает. И поэтому, для того чтобы нам, не называть вещи не своими именами, лучше изучить изначально нотацию и тогда Вы сможете нормально общаться с коллегами, понимать о чем идет речь.
В первом способе, мы создаем экземпляр класса MyClass по сильной ссылке. Что значит по сильной ссылке? Это значит, что у нас имеется переменная, которая содержит в себе ссылку, и если имеется такая переменная, которая содержит в себе ссылку, мы по ней можем обращаться к членам этого экземпляра, к членам этого объекта. В таком случае мы говорим, что создаем экземпляр по сильной ссылке. Ссылка, хранящаяся в переменной, является сильной. А как же сделать по слабой ссылке? Давайте создадим экземпляр класса MyClass, без переменой, без сильной ссылки. Такое создание называется – по слабой ссылке, потому что после вызова конструктора, мы можем поставить точку и обратиться только лишь один раз и только лишь к одному из существующих членов, которые входят в этот класс. Также хотелось бы сделать маленький акцент на том, что в IntelliSense, который является, в данном случае, механизмом автодополнения, который показывает интерфейс взаимодействия с нашим объектом, находятся еще незнакомые нам методы. Еще из курса Starter, мы знаем, что такие, фиолетовые кубики обозначают методы. Эти методы тоже присутствуют в нашем классе, но мы их сейчас не видим и будем изучать дальше. Поэтому пусть Вас не смущает, если в IntelliSense выводится больше методов или членов, чем мы можем видеть в нашем классе. Они также присутствуют в нашем классе, просто они здесь невидимые и дальше мы их визуализируем. Мы видим, что здесь находится field и Method(). Так вот, здесь мы создаем экземпляр класса MyClass, по слабой ссылке, и при этом мы можем нажать на точку(сейчас мы потеряли IntelliSense, для того чтобы его снова открыть, нужно нажать Ctrl+пробел), и например, вызываем метод или попытаемся обратиться к полю на запись, то есть здесь придется ему что-то присвоить, например, текстовое значение “Hello”. Дальше мы видим, что у нас все подчеркивается красным, потому что ссылка слабая, мы ею один раз воспользовались(как одноразовым стаканчиком), и в другом месте, мы уже не можем обратиться. Теперь давайте вернем все в обратно и посмотрим следующий пункт. То есть понятно различие между слабой и сильной ссылкой( мы еще будем делать на этом акцент). Можно сказать так – на 30 строке, мы создаем экземпляр с именем instance. Обратите внимание, если этот instance, содержит ссылку на экземпляр в памяти, то почему бы нам его не проассоциировать с этим экземпляром? Приведем пример, допустим нам нужно кому-то позвонить. Мы заходим в записную книжку в телефоне, и вводим в поиске нужное имя. После того как высвечивается нужный контакт, нажимаем кнопочку Функции-Показать и видим номер нужного абонента. То есть получается, что имя абонента, которое мы видим, это имя переменной. А когда мы заходим внутрь, чтобы посмотреть номер, мы переходим по ссылке. Ссылке на абонентский номер телефона. Теперь звоним этому абоненту, что-то сообщаем и завершаем разговор. Теперь давайте поразмышляем. Как же мы обратились к абоненту? Во-первых – удаленно. Представьте себе такую ситуацию 100 лет назад. Человек обращается к какой-то коробочке и называет ее именем абонента. Что подумали бы окружающие? Сумасшедший? Так вот обратите внимание – мы свой телефон, только что называли абонентским именем. Мы, переменную, которую видели, ассоциировали с абонентом. Так почему мы здесь не можем провести такую же самую ассоциацию? Обратите внимание – современная телефония устроена достаточно сложно. Мы не знаем как проходил наш сигнал, мы просто послали наше голосовое сообщение, в котором была закодирована какая-то команда. Так же и здесь. Что мы делаем? Мы посылаем некое сообщение, то есть, вызываем метод, мы заставляем наш объект выполнить какую-то команду. При этом, мы абстрагируемся, нам даже не нужно думать, как же проходит вот это сообщение, как происходит вот эта цепочка вызовов между экземплярами, объектами, как происходит пересчет адресов. Нас абсолютно не интересует модель сотовой сети и как доставлено наше сообщение. Нам главное, чтобы у нас была хорошая слышимость. Потому что, если абонента будет слышно плохо, мы позвоним оператору и оставим негативный отзыв. Также и тут – если Ваш метод будет плохо работать, не полностью выполнять возложенные на него обязанности, функциональность, которая в нем записана, то мы обратимся к разработчикам и скажем, что программа работает некорректно. Смотрите, как просто можно проассоциировать. Просто запомните, что это имя в телефоне, которое содержит в себе ссылку на настоящий объект. Как это происходит, нас пока не волнует. Теперь смотрим третий способ, как мы можем прокомментировать 30 строку. Мы инстанцируем класс MyClass. Почему мы часто говорим – инстанцируем? Instance – переводится, как экземпляр. Инстанцируем, можно дословно перевести, как экземплируем. Но в русском языке такого слова нету. Поэтому, мы используем английский транслит. То есть инстанцируем, трактуется как создание экземпляра класса. На 30 строке, мы инстанцируем класс MyClass. Можно продиктовать полностью. Мы создаем переменную, с именем instance, типа MyClass, и присваиваем ей адрес экземпляра на куче. Instance, является ссылкой, на экземпляр класса MyClass, построенной на куче. Вот это четыре способа комментирования этой строки кода. Наверное, мы уже достаточно уделили времени на рассмотрение процесса построение объекта, экземпляра. Мы рассмотрели различия между экземпляром и объектом. И мы переходим с Вами дальше.
Обратите внимание, что происходит у нас здесь. Здесь мы с Вами рассмотрим одновременно и методы доступа к полям, и модификаторы доступа. Модификаторы доступа, это специальные, ключевые слова, которые либо открывают доступ к члену класса, либо закрывают к нему доступ. Зачем так делается, сейчас мы обсудим. Вот у нас идет отдельный слайд, где мы сейчас пока изучим модификаторы доступа private и public. Этих модификаторов немножечко больше, но мы к ним будет подходить по мере изучения курса. Значит мы видим, что модификаторы доступа, private и public, определяют видимость членов класса. И смотрите, здесь у нас имеется предупреждение – “Никогда не следует делать поля открытыми, это плохой стиль. Для обращения к полю рекомендуется использовать методы доступа”. Значит первое правило – нам не следует делать поля public. А ведь мы уже делали? Но это было плохо. Теперь, мы будем стараться так не делать. В чем же здесь опасность? Мы с Вами помним, что у объекта имеется 2 природы: состояние и поведение. Представьте себе: Вы пошли в парикмахерскую, сделали себе дорогую прическу, затем Вы пошли на перекур, где кто-то на курилке увидел у Вас кусочек пепла. Этот “кто-то” подходит и начинает грубо стряхивать с Вашей головы этот кусочек, в результате чего, полностью разлохматил Вашу прическу. В этот момент, Вы, наверное, спросите этого человека:”Зачем Вы лезете к моему состоянию?”. На что Вам зададут следующий вопрос:”А что я должен(должна) сделать?”. Вы ответите:”Вам следовало, просто сказать обо всем мне, а не пытаться самому все исправить”. То есть вызовите метод. Вы же сами обладаете функциональностью и сами способны привести себя в порядок. Не принято вторгаться в личное пространство. Поэтому, мы видим что люди(объекты), взаимодействуют друг с другом, чаще всего, через использование поведения. Через использование вызовов методов друг на друге. И если мы вызываем метод, то это метод какого то указания(например, указания убрать кусочек пепла), на то что у Вас, в такой то области, имеется такой, допустим, объект, который нужно удалить. Важно помнить – состояние и поведение. Так вот мы понимаем, что получать доступ к состоянию – не всегда опасно. А есть еще более нежные и опасные состояния – например, если мы будем описывать сердце, печень, внутренние органы. Можно ли потрогать чье то сердце? Однозначно нет. А в каком случае и кто может трогать внутренние органы человека? Только специальные специалисты, и то не все, а только врачи-хирурги, которые учились и могут более-менее безопасно что-то у нас починить или что-то исправить внутри. Только они могут получать доступ к внутреннему состоянию. Если к внешнему состоянию можно, в некоторых случаях получать доступ, то к внутреннему, вообще нежелательно. Но в программировании, принято вообще все переменные делать private, то есть позначать модификатором доступа private. Условимся, что мы пока знаем 2 модификатора доступа- private и public. И вот эти модификаторы доступа, реализуют одну интересную парадигму ООП. Нам пора уже начать говорить о парадигмах. Мы знаем, что в ООП, имеется 6 парадигм. Первая парадигма – это действительно первая парадигма – инкапсуляция, вторая – наследование, третья – полиморфизм, четвертая – абстракция, пятая – посылка сообщений, шестая – повторное использование. Так вот, модификаторы доступа, реализуют одну из интересных парадигм – это инкапсуляция, как сокрытие реализации. Иногда, некоторые программисты, не совсем согласны с тем, что использование модификаторов доступа, мы относим к инкапсуляции. Они предпочитают называть использование модификатора доступа просто сокрытием реализации членов класса. И они правильно делают, потому что у нас, в информатике, имеется общее понятие, как сокрытие информации. Сокрытие информации разделяется на 3 части. Первая, это сокрытие реализации членов того же класса. Второе, настоящая инкапсуляция, которая называется инкапсуляция вариаций(когда мы, за нашими классами, скрываем части программных систем). И последняя, третья часть – сокрытие типов данных. Об этом мы говорили на курсе Starter, но поговорим еще. То есть посмотрите, такой подход, как сокрытие информации, состоит из трех основных частей – сокрытие реализации членов классов, инкапсуляция вариаций и сокрытия типов данных. Сокрытие типов данных, мы часто реализуем через использование ключевого слова var. Теперь мы подходим к нашему классу и смотрим: на 9 строке, мы создаем класс MyClass, на 11 строке, мы создаем закрытое строковое поле field и зануляем его. Теперь мы снова берем в руки телефон. Мы берем телефон и записываем в контактной книжке чьё-то имя, но мы не знаем номер этого абонента. И при попытке позвонить этому абоненту, телефон откажется дозвониться этому абоненту. Что это значит? Это значит, что мы создали переменную, но у нас нет нужного адреса(номера телефона). Вот и в этой строковой переменной нету ссылки. Мы не присвоили ей ссылку на экземпляр. Вы спросите:”На экземпляр чего?”. Обратите внимание, мы создаем переменную типа string. Давайте одним глазом заглянем в string. (Для этого нажмите F12) Смотрите, на 17 строке, мы создаем класс, с именем string. Оказывается, что все эти строковые типы, есть не что иное, как классы. И значит значения по умолчанию, для всех переменных, ссылочного типа(переменные, которые могут в себе содержать ссылки), а ссылка, есть не что иное как адрес экземпляра в памяти на куче. Такие переменные, мы называем ссылочные. Некоторые переменные могут просто содержать значение, например типа int. Если здесь мы создадим переменную int, которую назовем age, и присвоим 3, то при переходе внутрь int, мы видим, что здесь используется другая конструкция. Не класс, а структура. И поэтому переменные бывают двух типов: ссылочные и структурные. Но к структурам мы подойдем позже(у нас будет целой урок по изучению структур). И получается, пока, на 11 строке, мы создаем переменную ссылочного типа, потому что все объекты строятся на куче. А структуры, иногда, могут оказаться в стэке. Помните, мы говорили об области в памяти для хранения адресов возврата из вызываемых процедур? Так вот в этой области могут храниться и переменные. Но мы пока об этом не говорим(об этом будет целый урок). Значит смотрим, на 11 строке, мы создаем закрытое строковое поле ссылочного типа, потому что string – это класс, а не структура. Не забывайте об этом. На собеседовании обычно задают такие вопросы, начинающим программистам, на собеседовании, чтобы их подловить, узнать, насколько они увлекаются программированием. Опять таки, на 11 строке, мы создаем закрытое строковое поле field и присваиваем ему значение null(значение по умолчанию). То есть, пока нет ничего, пустота. На 13 строке, мы создаем метод и на 18 строке, мы создаем метод. Сейчас мы к нему подойдем. На 28 строке, мы создаем экземпляр класса MyClass с именем instance. Давайте попробуем обратиться к полю field. Как видите, IntelliSense не отображает field. Не пытайтесь печатать это поле вручную, так как программа все равно не заработает. Да и как мы знаем в C# не принято ничего печатать руками, для этого существуют подсказки IntelliSense. Если нужного элемента нету в IntelliSense, то скорее всего его вообще нету. Если Вы хотите вызвать какой-то метод – вызовите IntelliSense(Ctrl+Пробел) и выберите метод GetField(). Значит мы видим – так как это поле помечено модификатором доступа private, то мы его уже не увидим через точку на экземпляре. Оно просто не будет отображаться в IntelliSense. Обратите внимание! IntelliSense – это механизм автоматического дополнения. И получается, что принцип инкапсуляции, или мы будем говорить – принцип сокрытия реализации членов класса, как одна из парадигм. Потому что многие объединяют в себя понятия инкапсуляции, сокрытие реализации и инкапсуляцию вариаций. И вот этот принцип инкапсуляции реализован вот здесь, в текстовом редакторе. Это всего лишь правила Visual Studio. Это Visual Studio, которую мы видим перед собой и организует эту инкапсуляцию.
Хорошо. Раз у нас такое правило, что мы не можем напрямую обращаться к полям, как же нам тогда записывать в них значения? Вы скажете:”Нужно использовать методы доступа”. Вот у нас на 13 и на 18 строках создается два метода. Смотрите, первый метод имеет имя SetField. Этот метод (на 13 строке)имеет один строковой аргумент и в теле метода, мы полю field присваиваем значение аргумента. И вот такие методы, которые устанавливают значения полям, принято называть мутатором(метод-мутатор), от слова mutate- мутировать. То есть переменная изменяется, становится изменяемой. Такие методы, программисты, также называют сеттерами(setter). Мутатор или сеттер(правда, мутатором называют реже). На 18 строке, мы создаем метод-геттер(getter-получатель). Его еще называют аксессором(accessor). Некоторые программисты, называют оба этих метода аксессорами, но это не совсем правильно. Метод, который что-то записывает в поле – мутатор. Метод, который получает и возвращает нам значение – аксессор. В данный момент сюда можно сделать запись. Но если мы усложним наш метод, добавив проверку на вводимое слово(воспользуемся оператором if), мы обезопасим наше поле от каких-то нежелательных значений, которые могут в нее попасть. Далее смотрим – геттер. Геттер возвращает нам значение этого поля.
Давайте теперь почитаем, в теле метода Main код по созданию и работе с экземпляром класса MyClass. На 28 строке, мы инстанцируем класс MyClass, имя нашего экземпляра мы проассоциировали со словом instance. Далее, мы на 30 строке, мы на экземпляре instance вызываем метод SetField. Обратите внимание как мы произносим:”вызываем на экземпляре”, до этого, когда мы программировали на C++, мы говорили – “мы вызываем метод экземпляра”. Мы, как бы, пытались посмотреть внутрь объекта, внутрь класса. Если мы говорим ”метод экземпляра”, то мы уже подсознательно хотим посмотреть туда, но правило современных программистов говорит о следующем – вызываем IntelliSense(Ctrl+Пробел) и даже не смотрим на какие-то закрытые функциональности, которая находится в этом классе. Тут могут храниться много вспомогательных закрытых методов, которые вызываются в открытых методах. Это не наше дело. Не мы делали этот класс, и мы не будем смотреть в чужую работу. У нас много своей работы. Мы должны пользоваться только открытым интерфейсом взаимодействия. Мы должны обращаться к этому объекту только через те методы, которые нам позволено использовать. Например, Вы придете учится в живую аудиторию, и преподаватель, вместо того, чтобы читать курс еще убегает, начинает Вас толкать. Если Вы спросите преподавателя:”Что Вы делаете?”, он ответит Вам-“я играю с Вами в “регби”, на что вы заявляете, что находитесь здесь в качестве типа студент и не надо с Вами играть в регби. И поэтому не надо выполнять или пытаться выполнить ту функциональность, которая Вам не видна в IntelliSense. Если программисты, которые разрабатывали этот класс, откроют Вам эту функциональность – это другое дело. Поэтому, пожалуйста, руководствуйтесь тем, что Вы имеете в этом механизме автоматического дополнения. На 30 строке, мы на экземпляре instance, вызываем метод SetField() и передаем ему в качестве аргумента, строку “Hello world!”. Что здесь произойдет? Давайте традиционно пошагаем, как мы делали на курсе Starter. Смотрите, instance, пока равен null и как только мы создадим сейчас экземпляр – у нас произошла инициализация поля field конструктором. Теперь мы видим, что у нас здесь имеется ссылка(так она показывается). Если мы пытаемся приоткрыть, то мы видим, что поле field равно null, потому что конструктор и сам сюда записал null и мы здесь принудительно записываем null. То есть null – это и есть значение по умолчанию для полей ссылочного типа. То есть полей или переменных типа классов, потому что все их экземпляры размещаются на куче. И теперь мы перешли к методу SetField. Посмотрим как он работает. (Еще раз F11) Смотрим, value равно “Hello world!”, field пока равен null. После выполнения – field равен “Hello world!”. Идем дальше и теперь на 32 строке, мы создаем локальную строковую переменную с именем @string. Теперь присваиваем ей возвращаемое значение метода GetField(). Что он нам вернет? Давайте зайдем посмотрим. Оно вернет нам значение, которое хранится в поле field, а в поле field у нас хранится “Hello world!”. Вот, очень просто. И теперь мы переменной string(она еще пока равна null, заметьте, она еще ничего не вернула), присваиваем значение “Hello world!”. И на 34 строке мы выводим на экран значение строковой локальной переменной @string, которая хранит в себе возвращаемое значение метода-аксессора GetField(), который, в свою очередь, вернул нам значение поля field. Значит, обратите внимание, еще раз на этот пример. Здесь мы видим использование одновременно двух модификаторов доступа private и public. Если мы используем модификатор доступа private, то мы закрываем тот или иной член и уже программист-пользователь экземпляра нашего класса, не сможет получить к нему доступ. Все что помечено модификатором доступа public, является открытым и мы все эти члены увидим в IntelliSense. Но правила нам говорят, что все поля должны быть помечены модификатором private – то есть, должны быть закрытыми. Почему? Потому что, мы снова вспоминаем две природы – состояние и поведение. Состояние определяется значениями полей, поведение – хранящейся в методах функциональностью. И мы видим, что для того, чтобы организовать правильный доступ к полю, нам нужно создавать два метода – это setter и getter. Вы скажите, что в таком случае получается сложный код. Да, здесь абсолютно неочевидно и получается, что наши объекты представляют собой просто какие-то поведенческие сущности, которые нельзя потрогать. Есть ли какие-то механизмы, которые нам действительно позволят сделать такой “touch”(и безопасные механизмы)? Безусловно есть и мы скоро к ним перейдем. Есть такие, некие смеси “методо-поле”.
И мы переходим с Вами к следующему примеру и разбираем его. Вот как раз мы и подошли к этой интересной конструкции, о которой мы только что говорили. Эта конструкция правильно называется – свойство. Давайте посмотрим. На 12 строке, мы создаем класс с именем MyClass, на 14 строке, мы создаем строковое поле field, присваиваем ему значение по умолчанию – null, и на 16 строке, мы создаем, вот эту некую синтетическую конструкцию, которая вроде бы относится и к полям и к методам. Каким образом? Обратите внимание, что мы сделали внутри этой конструкции. Смотрите, на 16 строке, мы создаем, казалось бы, открытую строковую переменную, но у этой переменной, получается, есть тело. Правильно говорить так – на 16 строке, мы создаем открытое строковое свойство с именем Field. Часто эти конструкции делают так, чтобы имя совпадало с именем поля, с которым работает эта конструкция. Единственное отличие, как Вы видите – в заглавной букве. Потому что все поля, принято называть с маленькой буквы, а свойства и методы – с большой буквы. И мы видим, что внешне, оно просто напоминает поле, только название у него с большой буквы. Разворачивая его, мы видим, что у этого свойства имеется тело, а в теле – уже знакомые методы. И смотрите – если это удалить, у нас подставляется метод из предыдущего примера(метод-мутатор), а если вот здесь удалить – подставляется метод-аксессор, из предыдущего примера. Как Вы видите, здесь имеются практически те же методы доступа к полю. Значит, на 16 строке, мы создаем открытое строковое свойство с именем Field, в теле которого, мы создаем 2 метода доступа. Но обратите внимание на имена методов доступа. Мы setter(мутатор) уже просто называем set, потому что мы и так понимаем, что он вложен(относится) в тело свойства. А вместо getter, в свою очередь, просто называем словом get. Set и get – это ключевые слова языка C#. Обратите внимание – тело мутатора(setter), осталось таким же, мы просто убрали сложное имя. Зачем мы его убрали? Setter, сам по себе ничего не возвращает, соответственно нету смысла, указывать возвращающее значение, потому что он работает только на запись. Давайте посмотрим его использование. Обратите внимание, на 34 строке, инстанцируем класс MyClass, на 36 строке, мы обращаемся к свойству Field, при чем обращаемся мы на чтение. Если после обращения идет знак присвоения, то это значит, что автоматически срабатывает setter. Кто это определяет? Парсящий механизм(это такое правило языка). И смотрите, мы в field, присваиваем “Hello world!”. А как же здесь сработает вот этот параметр? Смотрите – value, превращается в ключевое слово, которое и будет равно вот этому значению, которое стоит после знака присвоения. Давайте мы его сейчас традиционно прошагаем(через F11). Так, инстанцируем класс, идем дальше, и вот, field пока равен null. Подождите, мы сейчас остановимся. Поскольку он туда не захотел зайти, мы сюда поставим брэйкпоинт и попробуем вот так прошагать. Вот, как Вы видите, мы вот здесь остановились. Полю field, мы еще ничего не присвоили – оно у нас равно null. А наводим на value, и видим, что value равно тому что стоит после знака присвоения при работе с этим свойством. field, пока равен null, но как только мы выполним, обратите внимание, мы в поле записали “Hello world!”. Давайте здесь тоже поставим точку останова(на getter). И теперь мы выводим на экран значение свойства Field. Мы понимаем, что свойство Field обратится к полю field и вернет нам его значение, и сделает это аксессор, который раньше назывался GetField(). Обратите внимание, что мы видим в этой программе. Мы видим, что мы создаем методы, но к этим методам, обращаемся так, как будто бы мы обращаемся к полю. У нас имеется некое имя и мы работаем с этим именем так, как будто бы мы работаем с настоящим полем. При этом здесь у нас может быть установлена некая защита. Как она может быть установлена? Очень просто. Сейчас снимем брэйкпоинты и смотрим: если value не равно “fool”, то мы полю field присваиваем значение вот этого, ключевого слова value, которое будет равно тому, что стоит после знака присвоения. Вы видите какая интересная искусственная конструкция? Но на самом деле, это искусственная конструкция. Ее в коде не существует. Это всего лишь удобство. Давайте посмотрим на устройство field, под рефлектором. Открыв с помощью него .exe файл, который получился в результате построения этой программы, мы заходим в класс MyClass и видим(модификатор доступа internal, мы его еще не учили) что мы создаем поле с именем field, здесь же мы создаем свойство(которое полностью повторяет свойство в этом классе). Вы спросите:”Как же его тогда нету в коде?”. Давайте посмотрим. Это тоже иллюзия, рисуемая нам рефлектором. Даже если мы посмотрим .Net 1.0 , все равно будет выводиться в виде свойства. Как же тогда увидеть методы? Смотрите, что мы здесь видим? Наш класс, невидимый конструктор по умолчанию, который здесь есть, поле field и свойство Field. Остается только нажать на этот плюсик. Что же мы видим? Что свойства скрывают в себе 2 настоящих метода, с настоящими полными именами. Вы видите метод set_Field()? Вот что в нем имеется, вот во что он трансформировался. Вот этот мутатор трансформировался в настоящий метод, просто от нас среда разработки это скрывает. Зачем? Чтобы упростить наше понимание, чтобы мы не задумывались о том, что это действительно какие-то методы. Но увидеть это мы можем, допустим в той же программе .NET Reflector. И тот же get_Field() повторяет все. И то, что мы видели в предыдущем примере – получается, что среда разработки, нам это сгенерировала. И поэтому, как мы видим, свойство Field, это искусственная конструкция, которая, в основном, присутствует только лишь для удобства. Впрочем, чтобы мы имели такой удобный синтаксис, такую удобную конструкцию, чтобы мы могли продолжать нормально обращаться к полям, как к полям, но при этом могли устанавливать, при необходимости, защиты. Помните, здесь мы делали проверку от слова “fool”? Еще раз: на 16 строке, мы создаем открытое строковое свойство именем Field, с двумя методами доступа – с сеттером и геттером. set работает на запись поля, get – на получение значения поля. Ключевое слово return Вы уже знаете еще из курса Starter.
Подразумевается, что Вы хорошо освоили тот материал, который подается в курсе Starter.
Смотрим дальше. Давайте посмотрим использование этих свойств. На 7 строке, мы создаем класс MyClass, на 9 строке, мы создаем private(закрытое) строковое поле field. Мы могли бы даже не указывать доступ к полю. Если Вы не указали модификатор доступа, то по умолчанию будет использован private. На 11 строке, мы создаем строковое свойство Field. Давайте мы еще посмотрим вот сюда. Если мы здесь откроем IntelliSense, то увидим, что в качестве значка у него установлен ключик. Вот таким ключиком и обозначаются свойства. В старых версиях, это была рука с карточкой, как Вы видели вот здесь в рефлекторе. Методы были вот такими розовыми. Вот этот замочек говорит о том, что это поле private. Когда ничего нету – оно является public. Смотрим теперь в код свойства. На 11 строке, мы создаем открытое строковое свойство Field, с двумя методами доступа – сеттером и геттером. В теле сеттера мы создаем условную конструкцию if и проверяем: если value равно “Goodbye”, то мы выводим – “Вы ввели недопустимое значение. Повторите попытку.”. В нашем случае, это было “fool”, давайте мы это и запишем. А иначе, мы присваиваем значение этому полю. Далее, на 21 строке, мы создаем геттер, метод доступа get, в теле которого мы проверяем: если field равно null(вдруг там вообще ничего не присвоено), то мы возвращаем “В поле field отсутствуют данные.”. На 25 строке: если(else if) field равно “Hello world!”, то мы возвращаем вот эту строку переведенную в uppercase(все большими буквами). Смотрите, мы на строке, вызываем метод ToUpper(). Что же это за метод и где же он находится? (Нажимаем правой кнопкой мыши – Go to Definition или просто F12) Смотрите, вот метод ToUpper(), и принадлежит классу string. Вот мы как раз посмотрели, что у него есть такой метод, который преобразует каждый символ в верхнем регистре. То есть, выведется “HELLO WORLD!”, иначе просто возвращаем значение поля field. Мы уже видим, что слово “fool”, мы уже не сможем записать. На 37 строке, мы создаем экземпляр класса MyClass. Теперь, здесь мы напишем слово “fool”, чтобы нам посмотреть, поскольку мы пытаемся недопустимое слово вписать. И на 40 строке, мы пытаемся вывести на экран значение этого свойства. Видите, мы здесь используем свойство - на запись в него, а здесь – на чтение. Теперь давайте выполнимся и проконтролируем ее вывод. Обратите внимание, мы видим, что мы пытались присвоить “fool” и сразу же получить его значение, вследствие чего система вывела “Вы ввели недопустимое значение. Повторите попытку.”. Далее, вот таким образом, мы подчеркиваем- выводим вот такую длинную линию, чтобы отделить наш вывод. Здесь мы создаем экземпляр класса string и создаем строку вот из таких символов. Видите одинарные кавычки? Со Starter, Вы помните, что это один символ типа char. Если наведем на конструктор – здесь подсказывает: первый аргумент это char, а второй – количество, вот таких вот символов. У нас их 50 штучек. Потому что, нехорошо рисовать 50 символов в коде вручную. Потому что, есть специальная программа, которая контролирует программистов, которая сразу же выдаст такое стилевое предупреждение, в котором показано, как надо сделать. На 44 строке, мы полю field, присваиваем “Hello world!”. А теперь, когда getter читает что-то и проверяет, действительно ли это “Hello world!”, он преобразует его в uppercase(вот в такие большие символы). Вы видите, как можно активно использовать свойства.
А мы переходим дальше. Смотрим: на 7 строке мы создаем класс с именем Constants, на 9-10 строке мы создаем два закрытых поля типа double(помните со Starter) и присваиваем им значения. Обратите внимание, что на 13 строке, мы создаем открытое свойство типа double и только лишь с одним методом доступа – только для записи(WriteOnly Property). Такие свойства, называются свойствами только для записи. Ниже у нас свойство только для чтения(ReadOnly Property). То есть, этот пример показывает, что у нас, свойства могут содержать в себе, только один из двух возможных методов доступа(либо setter, либо getter). Этот пример иррациональный – он просто показывает, что свойство может иметь только один из двух возможных методов доступа. Ну и теперь обратите внимание, что на 29 строке, мы инстанцируем класс Constants, на 31 строке, мы обращаемся к свойству Pi и присваиваем ему некое значение, а вот на 32 строке у нас идет попытка прочитать это Pi. Смотрите, он подчеркивает красным, а в списке ошибок пишет:Свойство индексатора(изучим позже), не может быть использовано в данном контексте из-за отсутствия аксессора. Теперь смотрим: если мы попытаемся присвоить что-то этому свойству(у него только аксессор, но нет мутатора), идет похожая ошибка, извещающая нас, что мы не можем это делать, так как это поле только для чтения. Смотрите какие простые, полезные, красивые и элегантные конструкции, которые называются свойствами. Свойство – синтетическая конструкция, которая состоит из имени, тела, а в тела еще вложены методы доступа – мутатор и аксессор, либо какой-то один из двух. Если будет только мутатор, то это будет WriteOnly свойство, если только аксессор – ReadOnly свойство.
Замечательно. Мы с Вами разобрали свойства и переходим дальше.
Сейчас мы разберем такую конструкцию, как конструкторы. Мы с ними уже работали, вызывали их, даже пытались разглядеть их в классах, но мы их пока не видели. Вот сейчас мы с ними уже познакомимся более подробно. Давайте зайдем в презентацию и посмотрим.
Что такое конструктор? Конструкторы бывают двух видов: конструкторы по умолчанию и пользовательские конструкторы. Главное запомнить правило: что, имя конструктора, всегда совпадает с именем класса. Если Ваш класс называется Butterfly, то и конструктор, тоже будет называться Butterfly. Это первое правило. Имя конструктора совпадает с именем класса. И второе правило: у конструкторов отсутствуют возвращаемые значения. Вроде бы как обычный метод, но нет возвращаемых значений. Но это не значит, что у конструкторов нет возвращаемых значений. Они есть, просто эти возвращаемые значения не пишутся при создании(визуализации) конструкторов. Почему? Потому что и так понятно, что они возвращают. Помните ключевое слово new, когда мы создавали объекты? Они возвращают ссылку, на тот экземпляр в построении которого, они(конструкторы) принимают участие. Поэтому, нам нет смысла здесь дважды писать MyClass, потому что он и так вернет ссылку на экземпляр MyClass.
Давайте перейдем к следующему слайду. И посмотрим вот такие правила.
Мы знаем, что у нас имеются 2 конструктора. Конструктор по умолчанию – у которого нет аргументов и пользовательский конструктор – у которого есть аргумент. Вот так классифицируются конструкторы. Задача конструктора по умолчанию – инициализация полей, того объекта который строится, значениями по умолчанию. Задача пользовательского конструктора(который с аргументами) – инициализация полей строящегося объекта значениями, предопределенными пользователем. Достаточно понятно. Что же это за предупреждение? Смотрите правило: если в классе имеется пользовательский конструктор(то есть, конструктор с параметрами), и при этом требуется создавать экземпляры класса с использованием конструктора по умолчанию(без параметров), то конструктор по умолчанию, должен быть определен(написан руками) в теле класса явно, иначе возникнет ошибка на уровне компиляции(когда Вы пытаетесь выполнить Вашу программу). Мы сейчас тоже к этому подойдем и посмотрим.
Вот теперь мы заходим в следующий проект – обратите внимание, теперь здесь, у нас, имеется 2 файла: Program(давайте в него зайдем) и Point(видим, что класс Point, мы храним в отдельном модуле(файле)). Часто вот эти файлики, когда мы на них смотрим в Solution Explorer, называют модулями. В C# есть 2 понятия модулей: первое понятие – это вот эти файлики, а другое понятие – это такая синтаксическая конструкция, которая называется модулем, которую на C#, а точнее в Visual Studio, мы использовать не можем. Мы можем написать модуль, но мы не можем работать с ним в Visual Studio. Где же мы можем с ними работать? Мы можем с ними работать без Visual Studio, обращаясь к ним напрямую к программке-компилятору, к которому обращается Visual Studio. Вот только так мы сможем разбить программу на модули. Вы спросите:”Зачем эти модули нужны?”. Очень просто. Бывает, программисты пишут программы на разных языках одновременно. И вот представьте, что у нас будет один проект на Visual Basic, а второй проект на C#.(это еще нормально) Представьте, что у нас многофайловый проект. Представьте, что вот этот класс написан на Visual Basic, а второй на C#. Разно языковые классы. [С модулями, Вы будете разбираться на курсе Professional, когда будете разбирать атрибуты. ] Так зачем же разбивать код по модулям? Очень просто. У нас есть 2 переводчика, как в реальной жизни. Представьте, что у нас англо-русский и немецко-русский переводчики. Так вот, если Вы обратитесь к компилятору C#, за переводом Visual Basic, у него не найдется, для Вас, определений. И поэтому, эти модули позволяют обращаться к нужным компиляторам, для того чтобы, при переводе, нашей“человеческой” программы, в переводе с английского языка в машинные коды, участвовал не один, а два переводчика. Но это настолько редкие случаи, что на практике, можно встретить довольно редко. Но, модулями, мы можем называть вот эти файлы, чтобы кого-то не смущать.
Значит, на 5 строке, мы создаем класс Point(переводится, как точка), создаем 2 поля: x,y. Мы понимаем, что это будут координаты. Создаем 2 свойства, заметьте, только для чтения. Мы не сможем, через эти свойства что-то записывать. На 22 строке, мы создаем конструктор. Обратите внимание – это конструктор по умолчанию, потому что, у него нет параметров. Имя конструктора совпадает с именем класса и у него нет возвращаемого значения. Потому что нет смысла писать вот так. По всем правилам, конструктор должен был бы выглядеть вот так. Но зачем, если мы и так понимаем к чему этот конструктор относится. Чем же занимается конструктор по умолчанию? Он инициализирует поля значениями по умолчанию. А какие значения у int? Нули. То есть, он невидимо для нас полю x присвоит 0 и полю y присвоит 0. На 28 строке мы создаем пользовательский конструктор с двумя аргументами. Пользовательский конструктор отличается от конструктора по умолчанию тем, что у него имеются аргументы. Обратите внимание, что в теле этого конструктора, мы обращаемся к нашим полям и инициализируем их значениями. Что такое this? Ключевое слово this – это ссылка на себя. Обратите внимание, у нас имена полей и имена аргументов совпадают. Ключевое слово this сообщает, что этот x относится к этому объекту, а вот этот x – относится к аргументу. Если мы уберем это ключевое слово, компилятор будет пытаться этому аргументу присвоить значение аргумента, что неприемлемо. А this переводится как :“этот класс” и в языке Basic используется как my. В C# используется ключевое слово this, чтобы сказать программисту, что мы используем именно поле, а не какой-то одноименный параметр. К примеру, this – ссылка на себя. Мы знаем, что мы, ко всем членам класса можем обращаться только через ссылку данного объекта. Понятно, что здесь пока такие “логические” адреса, которые потом будут пересчитаны. Что же мы видим: мы видим, что мы создали поле, 2 свойства только для чтения, конструктор по умолчанию, и пользовательский конструктор с двумя аргументами, в теле которого мы полям x и y присваиваем значения аргументов конструктора. Теперь обратите внимание. У нас здесь нет сеттеров. Потому что мы при создании точки, будем использовать либо конструктор по умолчанию( будет записано 0 и 0), либо пользовательский конструктор(если мы передадим 3 и 4, то соответственно запишется 3 и 4). То есть нам не позволено после создания этой точки, менять ее координаты. К примеру, чтобы не двигался график. Теперь давайте зайдем в Program и посмотрим, что происходит здесь. Здесь мы создаем экземпляр класса Point, обратите внимание, используя конструктор по умолчанию(без параметров). Далее, выводим на экран значение свойств x и y. Что сделал конструктор по умолчанию? Он присвоил полям нули, поэтому на экране мы видим нули. Подчеркиваем(30 раз). Создаем вторую точку и указываем 100,200. Значит, x присваивается 100, y присваивается 200. И соответственно, на 19 строке, нам выведется 100,200. Мы можем выполнить эту программу и посмотреть на результат ее работы. Обратите внимание, x=0,y=0, - это отработал конструктор по умолчанию. Когда у нас отработал пользовательский конструктор, с двумя аргументами, то мы здесь соответственно выводим 100 и 200. А теперь смотрите: у нас было правило – если мы используем пользовательский конструктор, но при этом в коде хотите использовать конструктор по умолчанию, то его нужно создать явно. Создать явно, это значит, что его нужно написать вручную. Давайте его закомментируем. Смотрите, что происходит: Visual Studio сразу уведомляет, что, не может вызвать этот конструктор, так как мы его заменили(перегрузили). Помните из курса Starter, что можно перегружать методы? Вот как можно перегружать методы, так же можно перегружать конструкторы, только здесь идет не столько перегрузка, сколько какое то перекрытие. Поэтому, если мы все-таки хотим этот конструктор использовать, мы должны написать его руками явно. Однако, мы его пишем явно только в том случае, когда мы используем пользовательский конструктор(конструктор с аргументами). Потому как, если бы мы не использовали конструктор с аргументами, то нам, соответственно, не надо было бы делать этот конструктор. Давайте еще раз выполнимся(F5). Смотрите, здесь у нас оттенился вывод. В теле конструктора мы выводим строки, чтобы оттенить именно работу конструктора, чтобы можно было увидеть, что они сработали. Что они не просто так здесь находятся. Результатом этой работы является вывод данных, в которых оба конструктора дают о себе знать.
Мы переходим к следующему примеру. В этом примере, у нас тот же самый код. Обратите внимание, здесь предлагается снять комментарий. Выполняем программу. И выясняется, что что-то не работает. В списке ошибок указано, что класс Point не содержит конструктор, который принимает 0 аргументов. Мы заходим сюда, смотрим – да, действительно, он закомментирован. Если мы снимем комментарий и выполнимся – обратите внимание, у нас все сработало хорошо. Этот пример был призван закрепить вот такое минимальное знание, о том, что важно помнить: если Вы создаете, пользовательский конструктор в коде, но пытаетесь вызвать конструктор по умолчанию, то, будьте добры, зайдите и напишите его ручками явно иначе у Вас будут проблемы с компиляцией Вашего проекта.
Мы переходим к следующему конструктору и смотрим, что у нас здесь имеется. Обратите внимание, на 7 строке, мы создаем класс Point, создаем три поля: x, y, name. Создаем 3 свойства, только для чтения: X, Y, Name. Дальше создаем конструкторы. На 34 строке, мы создаем пользовательский конструктор с двумя аргументами. В теле конструктора выводим на экран строку, чтобы оттенить работу данного конструктора и на 37, 38 строках инициализируем поля x,y значениями аргументов этого конструктора. Теперь смотрим на конструктор ниже. Здесь у нас имеется конструктор с одним параметром типа string. Но что при этом происходит? Если мы, из нашего программного кода, вызовем этот конструктор, с одним параметром, то этот конструктор, сразу же начнет искать по коду, конструктор, вот с такими параметрами. Видите, использование ключевого слова this, в конструкторе, приводит к вызову конструктора, с двумя параметрами, с 34 строки. Если мы здесь возьмем и уберем один параметр, то конструктор, не найдет в теле класса, конструктор с двумя параметрами. И прежде чем этот конструктор отработает(конструктор с одним параметром), он вызовет конструктор с двумя параметрами. Мы сейчас перейдем в программный код, поставим его вот здесь рядышком и видим, что в теле метода Main, мы создаем экземпляр класса Point и вызываем конструктор с одним параметром. Дальше у нас идет тестовый вывод. Теперь, давайте прошагаем эту программу. (F11) Смотрите, мы зашли в конструктор с одним параметром. Видите – name=”A”. Этот конструктор мы еще не вызываем, а теперь смотрите (нажимаем F11) – Вы видите, куда мы перешли? Мы сразу вызвали конструктор с двумя параметрами (видите this?). Использование ключевого слова this вот в этом контексте и в этом контексте имеет разное значение. Вот здесь this означает, что мы обращаемся к полю этого класса, а здесь this говорит о том, что мы вызываем конструктор. Вы спросите:”Почему же не придумали какое то новое слово?”. Это очень хорошо, что не придумали. Потому что, придумывание нового слова, в новых версиях языка, приводит к путаницам. Представьте, что у Вас есть переменная с именем name. У Вас эта переменная много раз встречается в коде. И тут Microsoft вводит ключевое слово name. И в новых версиях, все Ваши программы тут же перестают работать в новых версиях языка C#, потому что Вы уже не имеете право именовать свои переменные ключевым словом. И потому к придумыванию слов подходят с большой осторожностью. А учитывая, что есть программы очень большие и дорогие, в которых имеются миллионы строк кода и представьте себе: в один прекрасный момент, Вы переходите на новую версию, а у Вас новое ключевое слово начинает совпадать с Вашими именами переменных. У Вас появляются, к примеру, 30 ошибок, а эти ошибки влекут за собой еще ошибки. Представляете, сколько денег будет потрачено на тестирование и на исправление этих ошибок на ровном месте. Поэтому Microsoft и вообще разработчики языков программирования подходят очень осторожно к добавлению новых ключевых слов в новых языках и вообще к ключевым словам. И поэтому проще, чтобы парсящий механизм все-таки распознал контекст использования вот этого ключевого слова – то ли оно используется вот здесь, внутри конструктора, то ли оно используется вот здесь. Дальше мы еще будем смотреть варианты использования ключевых слов this. Это просто одноименные слова, которые выполняют разную роль. И мы видим, что вот этот this перебросил нас на вот этот конструктор, потому что он нашел здесь конструктор с двумя параметрами, который удовлетворяет, вот этим двум типам(int32). Теперь смотрите, мы его довыполняем. Здесь у нас произошла инициализация полей. И только потом, мы начинаем выполнять тело вот этого конструктора. Видите? Сначала сработал конструктор с двумя параметрами. Видите, как вывод оттеняет последовательности вызовов конструкторов? Теперь отрабатывает конструктор с одним параметром. И только теперь мы вывели на экран. Далее, мы инициализируем поле name значением этого аргумента name. Довыполнились. Видите? Вот у нас произошло полное построение экземпляра, объекта(Вы уже помните различия между ними) и далее нам осталось только вывести на экран значения полей, которые мы получили через свойства, только для чтения. Запомните эту разновидность конструктора. Называйте его пока просто this-конструктор. И поймите, что такой подход, позволяет нам вызвать имеющийся конструктор в нашем классе, главное чтобы у него совпадало количество и типы параметров как вот у этого this-конструктора.
А мы снова переходим к презентации и видим, что здесь у нас, как раз и описываются вызывающие конструкторы(мы будем сленгово говорить this конструктор, чтобы всем было понятно). Вот он в первую очередь вызывает вот этот конструктор, который мы видели в коде. Вот здесь, внимание, предупреждение: попытка вызова конструктора с не существующим набором параметров, приведет к ошибке уровня компиляции. Мы это с Вами видели. Мы здесь брали и убирали один параметр. Еще раз посмотрим. Или вот здесь еще можно добавить еще один параметр. Но мы лучше здесь уберем. Сразу появляется предупреждение: нельзя выполнить конструктор. С одним параметром еще сработает, с двумя – нет. Здесь подчеркивается зеленым, указывая на то, что это вообще не используется – Studio нам постоянно подсказывает. Если подчеркивает красным – критические ошибки, из-за которых программа не скомпилируется(не получим исполняемый файл), а зеленым – простые предупреждения.
Мы с Вами переходим дальше – у нас осталось совсем немного. Мы переходим с Вами к следующему примеру. Обратите внимание – этот пример, как показано на 18 строке, показывает, что мы можем передавать экземпляр класса в качестве аргумента метода. То есть здесь, на 7 строке, мы создаем класс MyClass, в теле класса, мы создаем метод, который ничего не принимает, ничего не возвращает(Выводим сообщение на экран, чтобы оттенить его работу). На 15 строке, мы создаем класс, с именем MyClass2, в теле которого, на 18 строке, мы создаем метод CallMethod(), который принимает один аргумент типа MyClass(видите, здесь подсветилось), с именем my и в теле метода, мы на вот этом аргументе, вызываем метод вот этого класса. То есть, мы видим, что в теле метода, мы вызываем метод, с именем Method(), на том экземпляре, который будет передаваться в качестве аргумента CallMethod(). На 29 строке, мы создаем экземпляр класса MyClass, на 31 строке, мы создаем экземпляр класса MyClass2 с именем my2 и на 33 строке, мы на экземпляре my2 вызываем CallMethod(), в качестве аргумента – передаем ссылочку на первый экземпляр. Мы можем выполниться и посмотреть результат работы. Видите: в итоге у нас вызван метод MyClass. Мы его вызвали вот здесь –на 21 строке. То есть получается, что мы вызываем вот этот метод, а в нем уже вызывается другой метод. Здесь просто показана такая простая техника, что мы в качестве аргументов методов можем передавать экземпляры тех классов, которые мы создали сами.
Мы переходим дальше. Мы сейчас рассмотрим с Вами еще одну интересную конструкцию – это автоматически реализуемое свойство. Обратите внимание, на 14 строке, (мы берем и вкладываем класс в класс – такое тоже возможно), мы создаем класс с именем Author и в теле класса мы создаем 2 автоматически реализуемые свойства. Обратите внимание, на 17 строке мы создаем открытое строковое свойство Name. Обратите внимание - у нас нет тел у методов доступа. Что же здесь происходит? Как же тогда происходит работа? Сейчас мы это посмотрим.
Давайте перейдем в презентацию. Мы видим, что в описании у нас здесь написано: при создании автоматически реализуемых свойств, компилятор создает закрытое, анонимное резервное поле. Что это такое? Смотрим. Если мы возьмем и дизассемблируем нашу программу, то мы увидим, что на самом деле, компилятор создает невидимым для нас два поля. Вот мы здесь дизассемблировали код класса MyClass, но мы можем посмотреть и на нашу программу в дизассемблированном виде. Вот представьте, что мы берем этот пример, заходим в класс Author, разворачиваем его и мы можем увидеть наш Name и Book, как в действительности они выглядят. На самом деле, здесь создаются полноценные свойства и вот создается 2 поля. Эти поля ставятся в соответствии вот этим двум свойствам: Name и Book. Обратите внимание, какие по умолчанию компилятор дал странные имена. Здесь мы видим, что даже в имени поля используются недопустимые символы. Вот такие имена компилятор дал этим переменным. Еще раз смотрим, здесь идем комментарий: при создании автоматически реализуемых свойств, компилятор создаст закрытое, анонимное резервное поле, которое будет доступно с помощью методов доступа get и set. То есть мы видим, что вот такие свойства, будут в итоге превращены в полные свойства. Зачем это делать? Мы уже с Вами говорили, что классы разрабатывают одни программисты, а пользуются ими другие программисты. Вот представьте себе, что когда мы разрабатываем класс, нам все равно приходится придерживаться принципа сокрытия реализации. Даже если наше свойство, казалось бы, пустое – мы в методах доступа get и set не ставим никаких условий, ничего не проверяем, а свойство все равно приходится делать. В итоге, пользователь этого класса будет работать со свойствами. Или я бы сказал, он будет работать с полями, через вот эти свойства, у которых геттер и сеттер, практически пустые. Так вот Microsoft предоставляет такую, более сокращенную конструкцию, для описания свойств, для удобства. Вот представьте себе, что вот этот класс разрабатывал я, а им пользуетесь уже Вы. Смотрите, на 23 строке, мы создаем экземпляр класса Author. Еще обратите внимание на одну интересную вещь. Мы здесь видим, что сразу после аргументных скобок конструктора, мы можем поставить блок в операторных скобках. Этот блок называется блоком инициализатора и в блоке инициализатора, мы можем обращаться к нашим свойствам для того, чтобы им присвоить какие-то значения. Не просто на экземпляре, как мы вот здесь делаем, а обращаемся в блоке инициализатора. Также, если наш вызываемый конструктор, является конструктором по умолчанию, то есть он не принимает, никаких аргументов, мы можем даже опустить аргументные скобки. Обратите внимание, на 29 строке, используется более сокращенный синтаксис. И снова же, в блоке инициализатора, мы производим инициализацию этих свойств, определенными значениями. Вот представьте теперь, что нам понадобится сделать какую-то проверку в свойстве Name. И теперь, невидимо для Вас, потому что Вы пользуетесь этими свойствами, мы расширяем геттер и сеттер. Здесь нужно создать строковую переменную. Вот, смотрите, мы ее создаем невидимо для пользователя и в геттере дописываем return name; , а в сеттере, мы пишем name=value; и здесь мы можем поставить какую-нибудь условную конструкцию. Например: если value традиционно не равно “fool”. Обратите внимание, что мы сейчас сделали. Мы вставили проверочку, при этом никак не сказывается на коде пользователя программиста, который пользуется этими классами. Теперь смотрите, получается, что программист, как пользовался этими свойствами, так и пользуется. Он даже не заметил того, что мы что-то поменяли в этом классе, добавили какие-то новые условия. Но в Book нам не надо ставить никаких условий, тут мы ничего не контролируем – главное, чтобы у нас, авторов не называли плохими именами. То есть вот как работает автоматически реализуемое свойство – такая удобная конструкция. Давайте еще раз зайдем в рефлектор и посмотрим, как выглядят автоматически реализуемые свойства уже после компиляции. Если мы выберем .Net 4.5 , то они нам покажутся вот в таком общем виде. Но это уже “фича” программы Reflector, который так научился отображать. А если мы посмотрим более ранние версии( это иногда полезно узнать как это выглядела эта конструкция в более ранних версиях языка C#), вот как она выглядит. Думаю, что с авто свойствами достаточно понятно.
Мы переходим к следующему примеру. Мы уже говорили, вначале урока, о сильных и слабых ссылках. На 7 строке, мы создаем класс с именем MyClass, в теле класса создаем открытый метод с именем Method() и на 19 строке, обратите внимание, мы не создавали никаких переменных – просто пишем ключевое слово new, то есть запрашиваем память на управляемой куче. Далее вызываем конструктор по умолчанию и после аргументных скобок, сразу же ставим точку и вызываем метод. Можно сказать по другому: на 19 строке, мы создаем экземпляр класса MyClass по слабой ссылке и вызываем на нем метод с именем Method(). Чтобы создать экземпляр класса по сильной ссылке, мы знаем, что мы должны были добавить переменную, которой присвоить ссылку на экземпляр вот этого класса и только в дальнейшем, обращаться к этой переменной. Вот как бы это было. Вот у нас, в данном случае, на 19 строке, мы создаем экземпляр класса MyClass с именем my(условно, это имя нашего экземпляра; будем ассоциировать имя переменной с самим экземпляром), по сильной ссылке. А в данном случае, мы создаем экземпляр класса MyClass по слабой ссылке. То есть, это всего лишь удобство – если Вам нужно один раз обратиться к этому экземпляру и больше нам он не понадобится, то это хороший способ, чтобы обратиться к этому экземпляру. Ну в данном случае, здесь есть еще кое-какие преимущества. Например, при работе со сборкой мусора. Но мы пока об этом не говорим. Сборка мусора – уничтожение объектов на куче, которую мы строим. Механизм сборки мусора рассматривается уже на следующем курсе(Professional).
А мы переходим дальше, чтобы рассмотреть просто пару интересных примеров, которые относятся просто к стилю использования классов. Давайте зайдем вот в этот документик. Обратите внимание - здесь у нас, в диаграмме классы представлены вот такими. Вот в таком, визуальном формализме. Все классы, которые используются в 12 проекте. Кто слышал об языке UML(Unified Modeling Language), в котором, мы можем вот такими прямоугольниками представлять классы, описывать члены этих классов? Видите, вот здесь написано: класс Body и в нем, мы видим, что у нас идет секция полей, секция свойств, секция методов. Вот так вот классы и описывает язык UML – унифицированный язык моделирования, но не программирования. И то, что мы с Вами видим – очень похоже на язык UML, но это не UML. Пожалуйста не путайте. То что Вы сейчас видите – отдельный уникальный язык Microsoft, который называется DSL(Domain Specific Language). И с UML, он имеет такое, легкое сходство, потому что и там и здесь прямоугольники, обозначающие классы, либо какие-то другие стереотипы. Мы уже видели структуры, дальше мы познакомимся с еще другими стереотипами. По крайней мере здесь, с использованием языка DSL, мы можем представить и посмотреть, какие у нас, в данном случае, имеются классы. А мы перейдем, посмотрим, что же у нас здесь происходит. Для того, чтобы изучать программу, ее можно изучать либо начиная с классов, либо начиная с использования этих классов. Конечно же, лучше начинать изучать программу с использования этих классов. Почему? Потому здесь более очевидно: смотрите, мы заходим в тело метода main(), видим, что на 9 строке, мы создаем экземпляр какого-то Title. На 10 строке, мы обращаемся к Title, к Content(видите свойство?) и присваиваем “Контракт”. Значит это какой-то объект, обозначающий какой-то документ. Получается, что здесь мы создаем тело(body) документа, нижнюю часть(footer). На 18 строке, мы создаем экземпляр класса Document, в качестве аргумента конструктора, передаем три эти части. На 19 строке, мы вызываем метод Show(), то есть отображаем документ. Давайте посмотрим, как оно работает. Обратите внимание: вот цветом вывелось “Контракт”, “Тело контракта” и имя директора. Вот мы и отобразили этот документ. Вы скажете что код выглядит ужасно- это же, представьте себе, что программист-пользователь, этого класса Document, должен сначала собрать из частей этот документ, потом в правильном порядке передать все эти части. Насколько ужасно и насколько неудобно, здесь ведется работа. Вот представьте, встречается нам вот такая программа. Хорошо, теперь заходим, например в Document. Смотрим: в документе у нас имеются 3 поля: title, body, footer – типа Title, Body и Footer. Имеется, как мы видим, конструктор, с тремя аргументами. В теле этого конструктора, мы инициализируем эти поля, значениями аргументов. Мы с Вами уже видели, когда вызывали этот конструктор, что мы передавали, в качестве аргументов построенные части. Эти ссылки, на эти части(Title, Body, Footer), присваиваем этим полям. Далее мы вызывали метод Show(), который, снова же, вызывал на каждую из построенной части, метод Show(). Как ужасно. Здесь как то все запутано. Давайте теперь посмотрим все по частям. Мы видим, что в классе Body(тело документа), у нас имеется некое строковое поле, которое будет содержать в себе контент тела документа. Здесь у нас идет свойство. Обратите внимание, на 11 строке, мы создаем метод доступа get с модификатором доступа private. И получается, когда мы будем пользоваться, то мы можем работать только на установку. Давайте сейчас попытаемся обратиться к Body и вывести напрямую, на экран, его значение контента. Смотрите – подсвечивается красным, сама студия уведомляет нас о том, что мы не можем этого сделать. Почему? Потому что не доступен метод доступа get, аксессор. Далее смотрим: имеется метод Show() и в теле этого метода, мы синим цветом выводим на экран содержимое тела документа и дальше возвращаем цвет консоли в стандартный серый. Если мы зайдем в footer, то увидим ноже самое – такой же самый вывод, все то же самое. Здесь также имеется Content, потому что у колонтитула тоже имеется свой контент. Вы спросите: где же используется этот контент? Давайте посмотрим. А вот где он используется – на 27 строке. Вот тут как раз и вызывается метод get. Метод доступа get, который помечен модификатором доступа private. И получается, что извне, мы класс не будем видеть этот метод доступа, а внутри, мы можем им полноценно пользоваться. И если мы зайдем еще в Title(заголовок), то увидим, что Title у нас выглядит также. Какая неудобно сопровождаемая программа. Что же в ней неудобного? Тут пользователь должен сам создать Title, Body, Footer, создать Document и добавить в него эти части. То есть, ему нужно понимать столько логики… Что же здесь можно предложить?
Вот как можно переделать эту программу. Смотрите: на 9 строке, мы создаем экземпляр класса Document, называем его “Контракт”, далее записываем “Тело документа” и подписываем фамилией. Вполне красивый стиль написания. И теперь отображаем документ. Нас не должно интересовать, из каких частей состоит этот документ. У нас есть свойство, где есть эти части, и с их помощью можно заполнить нужные разделы этого документа. Как же тут сделан сам документ, его части? Давайте посмотрим его части. Смотрите: Body осталось таким же как и было в предыдущем примере, Footer тоже остался таким же самым, как Вы видите, ничего не изменилось и Title остался таким же самым. То есть части документа мы не поменяли. Если мы зайдем в сам документ, то увидим следующее: мы оставляем все те же 3 поля Title, Body, Footer, но теперь, у нас имеется метод InitializeDocument(), в теле которого, мы создаем эти части. В конструкторе, обратите внимание, что мы делаем – он принимает Title, для красоты. Вот смотрите: он принимает в себя “Контракт”, у нас уже все это видно в конструкторе. Ну и перед тем, как проинициализировать свойство Content, Title-а, нашего большого документа, мы вызываем метод InitializeDocument(), который создает все его готовые части и только потом записывает. Здесь, есть возможность самостоятельно настроить, вписать какую то информацию. Для этого, мы используем 2 свойства, только для записи. Обратите внимание, у них имеются только сеттеры. Тут мы в body.Content присваиваем какое то значение, и в footer.Content присваиваем некое значение. Метод Show() также остался – он выводит все эти части на экран.
Давайте мы теперь зайдем в презентацию и посмотрим. Сегодня мы с Вами говорили еще о такой важной парадигме объектно-ориентированного программирования, как инкапсуляция. Мы с Вами разделяли такие понятия, как: сокрытие реализации членов класса и сокрытие реализации частей программных систем. Обратите внимание, видите – здесь Title, Body, Footer? В последнем примере, мы, как обычный пользователь класса Document, уже ничего не знаем о классах Body, Footer и Title. Это вспомогательные классы, мы знаем только общую информацию о документе и способах взаимодействия с этим документом. Потому что, если посмотреть предыдущий пример(12 пример), мы увидим, что последний пример выглядит лучше. Конечно же, для программиста-пользователя класса, выгоднее и удобнее использовать вот такой способ, без знания всяких частей. В 12 примере, мы должны сидеть, путаться, чтобы что-то не туда передать. Дизайн, в 12 примере, считается неприемлемым. Это даже не является объектно-ориентированным подходом. А вот в последнем примере, дизайн более-менее приемлемый. Здесь используется техники, как техника сокрытия реализации(мы уже видели, что у нас, в частях, имеются члены private). Мы помним, что сокрытие реализации членов классов, реализуется через использование модификаторов доступа. В данном случае, это public, private. А вот настоящая инкапсуляция, инкапсуляция вариаций, реализуется через использование вот таких объектов фасадов. Казалось бы, объект Document, не делает ничего полезного. Смотрите: вся польза находится в Body, Footer и Title. Они все здесь хранят, они подсвечивают тексты, в них все хранится, а Document ничего не делает – он просто, как то в себе, все это группирует. Но в итоге, вся слава достается именно Document. Правильно, ведь документа не существует. Документ это собирательное понятие из частей. Автомобиль тоже не существует – это тоже собирательное понятие из колес, дверей, руля, коробки передач, стекол, и так далее. То есть, это собирательное понятие вот этой сложной конструкции. Компьютера не существует, снова таки, это тоже собирательное понятие, вот так сконфигурированных пластмасок. Но тем не менее, устройство, которое находится перед Вами – компьютер/ноутбук. И даже если мы не будем знать какого то его устройства, от этого нам будет только проще работать. Также и здесь – чем меньше знаний мы имеем о внутреннем устройстве, какого то предмета, тем проще нам с ним общаться, то есть обращаться с этим предметом. И поэтому эта важная, первая парадигма – инкапсуляция, а точнее инкапсуляция вариаций и реализуется в последнем примере, где мы за вот этим, ассоциативным объектом Document(по сути он является ассоциативным объектом, потому что он в себе собирает документ из частей; делегирует некие запросы). Видите, его метод Show(), перевызывает методы своих частей. Он просто делегирует. Представьте: приходите Вы в сапожную мастерскую, а Вас встречает у окошка девушка. Вы говорите ей, что хотите поменять себе каблуки или отремонтировать обувь. Она у Вас принимает заказ, берет деньги, Вы уходите. Как Вы думаете, эта девушка буде сидеть и пилить Ваши ботинки и забивать гвозди? Нет, она скорее всего отдаст заказ мастеру. То есть что она делает? Она делегирует свою работу. Но в итоге, когда Вы приходите и забираете свои ботинки, Вы говорите спасибо этой девушке, которая оказала Вам вот такую услугу: приняла Ваш заказ и в итоге, отдает Вам готовую обувь. Вы мастера даже не видели в глаза. А представьте, если бы сидел мастер: с грязными руками – одной рукой пилил эти ботинки, и тут с Вами производил расчеты. Это было бы не очень комфортно, не так ли? Вот поэтому, вот эта девушка, которая принимает у Вас в мастерской обувь, вот она и делегирует эту работу – передает ее мастерам, которые уже умеют что-то делать. Также и здесь: наш Document ничего не может делать, но посмотрите – он предоставляет нам, помимо чистой инкапсуляции, еще и абстракцию. Абстракцию, как собирательное понятие, такой сущности, как документ. Мы с Вами также рассмотрели первую парадигму ООП – инкапсуляция вариаций, а также, немножко затронули абстракцию. Формирование абстракций, как собирательное понятие.
Замечательно. Давайте вспомним, что мы сегодня рассмотрели. Перейдем к презентации. И подведем итог нашего урока, потому что наш урок закончен. Мы с Вами разобрали объектно-ориентированные парадигмы. Разобрали объектно-ориентированное программирование, что это такое. Познакомились с такими основными сущностями как: классы, объекты, потом мы поняли, что такое экземпляры и чем экземпляры отличаются от объектов. Посмотрели на использование модификаторов доступа private и public. Использование которых реализует достаточно важную идею сокрытия реализации членов класса. Познакомились с такой конструкцией языка C#, как свойства, проанализировали их работу. Под Reflector-ом посмотрели, что любое свойство, всего лишь 2 метода, просто удобно представленных. Мы поняли, что у нас имеются 3 разновидности свойств: полное свойство, которое содержит оба метода доступа(и get и set), и свойства только для чтения или только для записи. Далее, мы разобрали с Вами такие понятия, как конструкторы. Мы понимаем, что конструктор – специальный метод, основной задачей которого является инициализация полей строящегося объекта либо значениями по умолчанию(если это конструктор по умолчанию), либо значениями предопределенными пользователем, если это пользовательский конструктор(с аргументами). Далее мы посмотрели на такую разновидность конструкторов, как конструктор, вызывающий другой конструктор(слэнгово можно сказать – this-конструктор). Далее, мы рассмотрели еще одну интересную разновидность свойств – автоматически реализуемые свойства. Мы под Reflector-ом посмотрели, во что же они в действительности превращаются после формирования исполняемого файла, после прохождения всех процедур компиляции. Посмотрели отличия между сильными и слабыми ссылками. И в конце урока, мы с Вами посмотрели, уже на примерах, как же реализуются идеи инкапсуляции вариаций, то есть инкапсуляции, которая позволяет нам скрыть части программных систем от пользователя, затем, чтобы упростить работу с объектом и с его конфигурированию сложного объекта. Мы видим, что вот этот класс Document, у нас как раз и реализует вот эту идею инкапсуляции вариаций. Он является неким фасадом, неким ассоциативным объектом, который за собой скрывает вот эти объекты: Title, Body, Footer и сразу же он реализует еще одну интересную парадигму – абстракция, как собирательное понятие, какого то формирования. Как формирование высокоуровневой сущности из частей.
На этом наш урок подошел к концу. Спасибо за внимание, до новых встреч.