×
Ви дійсно бажаєте відкрити доступ до тестування за курсом C# 5.0 Стартовый на 40 днів?
ВІДЕОУРОК № 7. Методы в C#
Здравствуйте! Тема нашего сегодняшнего урока – методы. Что такое методы? Методы – это специальные конструкции языка C# которые позволяют группировать в себе программные коды. Какие? Мы уже знаем, у нас имеются переменные, условные конструкции, циклы. Метод – это некий контейнер, который может в себе содержать различные программные коды. А зачем это делать? Зачем нам группировать коды? Мы сейчас к этому подойдем. Давайте мы перейдем к следующему слайду и представим, как может работать метод в общем.
Обратите внимание, у нас имеется вот эта странная конструкция. Представьте себе, сто метод – это кибернетический черный ящик. А что такое черные ящики в кибернетике? Это функциональные преобразователи. У которых есть вход, на вход что-то подается. Внутри находится скрытый механизм, потому он и называется черный ящик, потому что мы не знаем, что в нем происходит. Для многих компьютер может являться черным ящиком. На вход мы с клавиатуры что-то подаем, а на экране выводится. Так вот метод представляет собой некий черный ящик, который содержит в себе некий механизм обработки данных. И у методов имеется вход и выход. Ну не у всех методов. Мы сейчас рассматриваем в общем виде. И вот представьте себе, что я беру один байт. Какое здесь лежит значение? 1. И бросаю его на вход этого черного ящика. Этот механизм закрутился, что-то он с ним сделал и на выходе дает мне какое-то значение. 2. И при этом этот ящик, функциональный-преобразователь он имеет какое-то свое имя. Зачем? Потому что у нас может быть много таких блоков с кодом, который мы будем использовать и к этим блокам с кодами мы будем обращаться по имени, и поэтому метод – это некая конструкция языка C#, которая содержит в себе некую функциональность. Мы к этой конструкции можем обращаться из наших программный кодов. Но сейчас мы к этому подойдем. Обратите внимание, метод может в себя что-то принять, переработать, представьте, что здесь можно было сделать? Ну тут скорее всего идет инкремент. Если мы сюда подаем байт, он сразу инкрементировался и выкинули уже увеличенный на 1. Возможно и так, это самый простейший способ предположить, что же здесь происходит. Как бы мне было интересно заглянуть и посмотреть, что же здесь происходит. Давайте одним глазом заглянем в этот ящик. Смотрите, ага, что у нас здесь имеется? Обратите внимание, вот это имя Addition – это имя черного ящика. То есть наш ящик называется Addition. Справа от имени в круглых скобках у нас идет описание этого входа. Мы говорим, что на вход мы можем подать что-то однобайтовое, однобайтовое значение. А слева от имени у нас идет тоже тип, который описывает тип значения, который выйдет вот отсюда. Обратите внимание, вот эта строка называется сигнатурой метода. Сигнатура в нашем случае состоит из имени метода. Имя этого ящика – Addition – сложение. Из параметров, потому что это отверстие символизирует что-то входящее, входное, параметры, или как еще принято называть – аргументы. Это синонимы. Я чаще говорю – аргументы, кто-то говорит параметры. Так, значит мы здесь записываем тип и имя параметра, которые мы должны передать. А вот это? Это тип возвращаемого значения. Что же этот ящик должен вернуть. Забрасываем в него яблоко, на выходе баночка с джемом. На вход поступил килограмм яблок а на выходе литр яблочного джема. Получается, что вот этот ящик занимается тем, что перерабатывает яблоки в варенье. Значит еще раз смотрим мы создаем метод или можем сказать функцию. Мы сейчас разберем эти отличия. Создаем функцию и имени Addition. Смотрите, эта строка называется сигнатурой метода. Сигнатура метода описывает имя метода, его параметры и возвращаемое значение. Но подождите, с третьей спецификации C# в сигнатуру не входит возвращаемое значение метода. Но пока еще будем говорить по старинке, не совсем правильно, не совсем в свете новых спецификаций. Мы пока будем называть всю эту сроку сигнатурой, а потом будем делать поправки. В нашем случае сигнатура включает в себя имя метода, или его еще называют идентификатор, это то что большими буквами написано на ящике. Вот у меня на компьютере написано четыре больших буквы S, O, N, Y. Это параметры, то что я нажимаю на клавиатуре, а вот это то что выводится на экран. Мы создаем метод с именем Addition, который принимает один аргумент типа byte и возвращает значение типа byte. Обратите внимание, далее идет блок операторных скобок – это тело метода. Ага понятно, значит вот этот блок в операторных скобках, он и содержит в себе полезную функциональность. Механизм, настоящий механизм, который сделали инженеры. Во это – то что написано на ящике, вот это – то что мы можем засунуть в эту дырочку, потому что если мы в машину для варенья, миксер, кухонный комбайн вместо яблок забросим подшипники. Что будет с вашим кухонным комбайном? Все ножи поломаются, все ножи покрошатся. Поэтому вот этот параметр описывает какой тип можно передать в метод. Мы здесь видим, будьте осторожны, здесь только gently types, нежные типы и на выходе мы получаем тоже очищенные яблоки или варенье. То есть мы видим, что этот тип описывает а что же на выходе вы получите. Вы не ожидаете что ваши яблоки тут попадут на философский камень и он сделает с них золото, и здесь пойдут золотые кольца. Нет. Ожидайте байт. Засуньте сюда байт в этот миксер и получите на выходе модифицированный байт. Наш метод состоит условно из двух частей. Первое, это сигнатура метода, и его тело. Тело содержит определенный механизм. Это может быть даже 10, 20, 100 строчек кода. Здесь может производится еще какой-то сложный расчет, эластичность спроса по цене или какие-то теоремы, что угодно. Соответственно, если здесь наш метод занимается сложением, то он и должен называться метод-сложение – Addition. Потому что если мы в магазине будем продавать калькулятор и назовем его – чай, или кофе. И сделаем коробочку, на ней напишем «Чай», а внутри будет лежать калькулятор. Это правильно? Все будут думать, что это не калькулятор, а чай. Будут покупать его, приходить домой, распаковывать и разочаровываться, потому что они не получили того продукта, которого хотели. Так же и с методами, им нужно давать такие имена, чтобы эти имена соответствовали роду выполняемой деятельности этого метода. Еще раз смотрим на метод. Метод состоит из двух частей: сигнатура метода и его тело. Сигнатура, сама по себе, состоит еще из трех частей: это имя метода, набор аргументов, параметров, которые говорят, что можно поместить в этот метод, и возвращаемое значение. Но снова же, давайте вспомним новую спецификацию, возвращаемое значение уже не входит в сигнатуру метода. Мы же начинающие, мы можем позволить себе делать такие поправки. Хорошо, давайте теперь посмотрим, раскрутим этот черный ящик и посмотрим что в нем находится. Когда к нам на вход поступает один байт, смотрите, что мы делаем, мы увеличиваем его на 1. Можно было бы просто сделать что? Argument++. Применить инкремент, правда? И если мы сюда на вход подаем 1. Argument – это как переменная, чему она равна? 1! Значит переменная argument будет равна 1. Значит мы к этому параметру добавляем 1. В итоге чему он равен? 2. Дальше, обратите внимание, ниже ключевое слово return – возврат, вернуть. Вы видите, ключевое слово return возвращает значение этого параметра. И чему оно равно? 2. Он выкидывает из себя двойку. Просто? Просто. Смотрите, какие интересные конструкции. А зачем же и где они используются? Подождите, мы как всегда немного рассмотрим, а потом уже будем смотреть их применение. Ну или есть все-таки желание посмотреть, как это выглядит в коде? Ладно, давайте отойдем от плана урока, откроем Visual Studio. Значит, что нам нужно сделать? Создать новый проект. Берем создаем новый проект. И какой мы создаем? Консольный. И даже не будем его переименовывать. И для того чтобы нам создать метод, нам нужно выйти за пределы этих скобок. Обратите внимание, я выделил этот блок. Давайте, мы его выделим традиционно через Shift+Alt+стрелочки. Смотрите, это метод с именем Main, который ничего не возвращает – void – пустота. Имеет один параметр. Но мы пока разбирать его не будем. Обратите внимание, все методы должны быть вложены в еще более высокоуровневые контейнеры – в классы. Но классы мы уже точно не будем разбирать, потому что классы мы будем разбирать на следующем курсе, потому что он будет начинаться с рассмотрения классов. Хорошо. Нам нужно создать метод. Для того чтобы нам создать метод, смотрите, что мы делаем. Мы берем и вот здесь в области класса за пределами нашего метода Main начинаем создавать метод, который и будет складывать два числа. Иди давайте мы сделаем такой метод, который у нас был вот здесь. Почему бы нам не создать именно его. Ну к примеру, или похожий на него метод. Что мне нужно сделать для этого? Скажете, ну сначала нужно написать имя… Нет! Представляем комплексно, вход – выход. Или кухонный комбайн например, забросили – получили что-то. Условимся, что этот метод будет выполнять какую-то арифметическую операцию. Что он будет возвращать? Int. И назовем этот метод Add к примеру. Теперь ставим параметры, и теперь чтобы нам сложить два числа, нам нужен не один вход, а нам нужно два входа теперь, потому что мы хотим одно число передать и второе число передать. Да? Хорошо. Давайте попробуем так сделать. Вот мы берем и передаем тоже сюда int a, запятая, мы их через запятую ставим параметры, или мы из называем аргументы. И int b. Смотрите, мы создали некую сигнатуру. У нас имеется имя метода, аргументы, то есть параметры, который будут внутри ящика, и то что мы ожидаем получить на выходе из этого ящика, хорошо, теперь что вы говорите нужно сделать? Тело, блок? Хорошо, давайте. Открывающая и закрывающая операторные скобки. Смотрите, подчеркивает красным, что-то ему не нравится. Давайте посмотрим, что ему не нравится. Так, а смотрите, он ничего не передает, видите, нужно ключевое слово return написать. Пишем return. Что мы хотим сделать. Мы хотим а + b. Просто? Просто. Теперь нам нужно сделать еще одну маленькую поправочку. Метод у нас сработал хорошо. Мы нажимаем клавишу F6. Смотрим внизу. Build succeded. Давайте выполнимся. Теперь нам как-то нужно заставить работать. А что он здесь отдельно лежит, бесполезно. А смотрите, что мы сейчас делаем. Мы здесь должны сделать небольшую поправку. Смотрите, вы это сейчас увидите. Мы берем int, создаем переменную с именем sum и присваиваем ей… Смотрите, не видно нашего метода. Поэтому пока, так как наш метод main имеет вот такой модификатор static. Мы еще не знаем, что это такое, но мы скоро познакомимся. Мы берем, и вот здесь к нашему методу приписываем static. Попробуем сейчас. Смотрите, вот он у нас появился. Так Add(); Опять красное. Что же это такое? Давайте смотреть. No overload for method 'Add' takes 0 arguments. Он имеет 0 аргументов. Точно, мы забыли передать сюда значения. 2 и 3. Интересно, сколько будет 2+3. Далее мы хотим вывести. Что мы делаем? CW Tab+Tab. Мы выводим на экран sum. И для того чтобы нам все это увидеть, что нам нужно сделать? Console.ReadKey(); Давайте попробуем выполнится теперь. Результат выполнения операции сложения мы видим на экране. Вы скажете, что еще не до конца все понятно. Если нам не до конца все понятно, то что нам нужно делать? Шагать? Давайте пошагаем. Какая комбинация? F11. Смотрите, так, точка входа. Мы всегда начинаем выполнение программы с метода Main в C#. Смотрите, встали на этой строке. Sum пока равна 0. Куда мы попали? Мы попали внутрь метода Add. Давайте-ка наведем на а. Смотрите, а равно 2. Вы видите? Этот параметр равен двум. Так, а чему равен b? И равен 3. Смотрите, как символично. И теперь а+b будет чему равно? 5! Смотрите, оно даже подсказывает, что сумма аргументов метода Add будет равна 5. И ключевому слову return остается только взять как из рогатки выстрелить и он вылетит из нашего метода. И куда он попадет? А вот куда – в sum. Метод Add выбрасывает из себя возвращаемое значение и переменная sum принимает его в себя. Давайте дошагаем. F11. Смотрите, мы стали здесь, переменная sum пока 0. Теперь sum равна 5. Мы теперь на экран выводим возвращаемое значение метода Add, которое мы поместили в переменную sum и вот дальше мы ее выводим на экран. А зачем такие сложности? Ладно, посмотри еще. Представьте себе, что у меня, помните мы с вами рисовали прямоугольники? Берем удаляем Add. Прямоугольники и звездочки рисовали. Давайте посмотрим, как мне нарисовать 10 на 10. Так, for Tab+Tab. Видите, что произошло? Я написал for Tab+Tab. У меня все само создалось. Здесь берем и пишем 10. Внизу в цикле еще раз пишем for. I нужно заменить на j. Поставить другие индексы, чтобы они не совпадали. J<10. Здесь что мы делаем? Пишем Console.Write(); И здесь мы делаем звездочку. Помните? А ниже, то есть мы видим, что for рисует что? Строку из звездочек, внутренний for, а внешний? Переводит все на новую строку. Для того, чтобы перевести на новую строку пишем cw Tab+Tab. Вот этот метод WriteLine, он как раз и переведет курсор на новую строку. Замечательно. Теперь мне нужно нарисовать очень много разного размера прямоугольника. Так, копипастим… Вы так не делайте. И теперь нам надо как-то все это настраивать. Что-то я здесь перепутал. Нет, я так не хочу их рисовать. Тем более мне нужны другие. Нет, этот код вообще читать невозможно будет. Что вы мне подсказываете? Метод? Метод Rectangle? Да? Давайте попробуем. Хорошо. Этот метод у нас ничего не будет возвращать, хорошо? Void – пустота. А что же это за пустота? Давайте посмотрим, F12. Смотрите, просто пустая структура void. В ней ничего нет. Помните, мы когда в int заходили? Давайте зайдем в int, посмотрим внутрь. F12. Смотрите, сколько много всего, всякой полезной функциональности, MaxValue, MinValue. А когда мы заходим в void, у нас тут ничего. И поэтому этот тип символизирует пустоту. Символизирует ничего. Представляете? Мы об это еще поговорим. Это все равно, что я вам говорю, чтобы вы протянули руку. Вы протягиваете, я что-то в кулаке подношу, открываю кулак, а там пусто и говорю: «На, возьми ничего.» Хорошо. Имя этого метода мы назовем как? Rectangle. Я вам говорил, что мне нужно рисовать самые разные прямоугольники. Не только 10 на 10. Вы скажете что? Что здесь нужно два параметра. Вот, смотрите, один параметр и второй параметр. Давайте сделаем int а, и int b. Хорошо. Тело. Можно мы скопипастим? Так, пока никто не видит мы это сделаем. Смотрите, мы перенесли сюда нашу функциональность по отрисовке прямоугольников и теперь вот эту десятку мы заменяем на a – это сколько строк будет, а вот эта b означает сколько будет звездочек в строке. Ага, давайте тогда мы назовем из по-другому, потому что они не красиво называются неявно. Значит у нас есть height – это высота. И ширина – width. И теперь мы вот здесь подставляем что? Height. А здесь? Width. Чудесно. Смотрите! И теперь вот этот параметр будет подставляться вот сюда. А вот этот width будет подставляться вот сюда. Давайте попробуем нарисовать прямоугольник, ну например, 5 на 12. Что нужно сделать? Мы вот здесь забыли дописать Static. Это пока, потом вы поймете что это такое. Это просто особенность использования этого метода в классе Program и метода Main. Делаем пока поправку такую. Теперь смотрите, мы вызываем Rectangle. Сколько мы там хотели? Ставим скобку. Смотрите, оно мне подсказывает height и width. Значит height мы сколько хотели? 5, в высоту. А в ширину сколько? 10? 15! Давайте выполнимся. F5. Смотрите, у нас нарисовался этот прямоугольник. Вы видите что происходит? Ниже под ним мы хотим нарисовать еще один прямоугольник. Давайте это сделаем. Rectangle(), а здесь например высотой в 20, а шириной в 50, например. Почему нет? Выполняемся. Смотрите, вы видите, что у нас происходит. Мы обращаемся к одному блоку функциональности, а он смотрите, какие чудеса вытворяет. Выдает совсем различный вывод. Удобно? Удобно. Так обратите внимание, как называется вот эта техника, когда мы один раз сделали, а потом много раз использовали? Reusable – повторное использование. Это основа повторного использования, самая маленькая. Дальше мы по повторному использованию будем говорить еще много, когда перейдем к объектам, классам. Ну а пока это самые азы повторного использования. Поэтому и говорится, что методы, это многократно используемые конструкции. Мы ее один раз делаем, задаем логику. Видите? И какая логика у нашего метода? Отрисовывать прямоугольники. Ну а теперь давайте исправим еще одну важную ошибку. Смотрим, где она? Где? Нет. Нет. Имя. У этого метода просто ужасное имя. Методы должны называться как? Глаголами, и причем это должен быть инфинитив, который отвечает на вопрос: «Что делать?» И причем это инфинитив без частицы to. ToDraw – например. Мы говорим: «Нарисовать Rectangle!» Хорошо. Поэтому нам нудно переименовать его. Для того, чтобы его переименовать мы попробуем прорефакторить. Берем Refactor\Rename. Я не хочу все здесь переписывать. И давайте мы допишем здесь DrawReactangle. Что сделать? Нарисовать прямоугольник. Смотрите, мы здесь отмечаем, чтобы везде просмотреть и поменять и в комментариях и в использовании. ОК. Смотрите, здесь мне подсказывается что я буду переименовывать, идут подсказочки, что у тебя вот здесь еще. Хочешь ли ты их переименовать? Видите? Смотрите, мы его переименовываем. Apply. Смотрите, все переименовалось, смотрите, как удобно. Какая волшебная студия, что она может делать. Мы с вами отошли от темы нашего уроки и немножко сами так побаловались и покодили чуть-чуть, посмотрели. Ну, а теперь мы возвращаемся к нашей презентации, чтобы нас никто не заподозрил, что мы… Это мы даже закрываем, удаляем. Хорошо. А мы идем дальше. И на следующем слайде мы посмотрим, какие же у нас бывают разновидности методов. Еще раз посмотрим на этот ящик. Смотрим, здесь имеется один вход, имеется выход. А бывают ли такие методы, у которых нет входа? Что мы ничего не передаем. А может быть есть такие, у которых нет выхода. Давайте посмотрим, на их разновидности. Обратите внимание, вот все возможные формы создания методов. Методы могут быть как с параметрами, так они могут и ничего не принимать в себя. Методы могут как что-то возвращать, так они могут ничего и не возвращать. Обратите внимание, на название этого слайда: «Функции и процедуры». Так вот методы логически принято разделять на функции и процедуры. Функции – они возвращают значение. Функции, это такие методы, которые имеют возвращаемое значение. А процедуры – это такие методы, которые ничего не возвращают. Видите тут закрыты эти дырки. По вхоящим параметрам Мы не классифицируем методы. Дело в том, что не во всех языках принято различать методы и разделять их на функции и процедуры. Но вообще в информатике это принято делать. В C# функции и процедуры практически не различаются. Я вам скажу больше. Физически процедур в C# не существует. Потому что вспомним если мы ничего не хотим вернуть, то мы пишем void – пустота. Но тем не менее мы в нее заходили в эту пустоту. Она осязаема. Да, это отдельная конструкция языка, которая описывает эту пустоту. Помните void. Мы к этому еще подойдем, не переживайте Если вы забыли. Так вот, технически в языке C# процедур не существует, но логически мы их учитывать должны. Все методы, которые возвращают void – пустоту, помните я говорил протяните мне руку, так вот такие методы мы у себя в голове будем называть процедурами. А функциями? Функциями мы будем называть те методы, которые будут в действительности возвращать какие-то значения. Обратите внимание, функции в математике и функции в программировании. Смотрите, они даже чем-то похожи. У функций имеется имя, например f. Есть параметры, и есть возвращаемое значение. Результат этой функции. Например мы говори f(x) = x^2. Что будет возвращать такая функция? Квадраты аргументов. Помните, мы рисовали такую табличку. Так разделяли ее и здесь шел у нас икс и игрек. И школе мы ее называли таблицей соответствий аргумента функции. Или другие представления. Мы рисовали такое овалы, а между ними проводили линии. Графы соответствий строили. Хорошо. Не будем долго о математике. Значит условимся, что логически принято методы разделять на функции – это те методы, которые что-то возвращают, и на процедуры, которые ничего не возвращают, то есть в нашем случае возвращают пустоту – void. Запомните это. Мы уже заходили в этот void. Видели – пустая структура. Но и еще будем заходить. Вспомнили? Чудесно. Вот четыре возможных вариации создания методов. Вот такой, который принимает один и более параметров и что-то возвращает, который ничего не принимает. Пустые скобки и что-то возвращает. Который принимает один и более параметров и ничего не возвращает. И такие, которые ничего не принимают и ничего не возвращают. Иногда встречаются нам устройства, у которых нет сопровождаемых узлов. Вот например, разновидность компьютеров. Есть такой компьютер, там еще фрукт такой нарисован надкушенный. Не будем произносить это имя. Хорошо. Мы же все-таки Microsoft ориентированные программисты. В том компьютере практически не обслуживаемых узлов, он так запечатан, что ничего не можем там поменять. Так же и здесь. Бывают такие методы, которые заявляют, что они ничего не принимают, и ничего не возвращают. Они выполняют какую-то общую деятельность. Хорошо. И мы переходим с вами еще к последнему слайду. И вспоминаем, как же у нас создается метод и из чего он у нас состоит. Именно метод, как конструкция языка C#. Обратите внимание, у нас имеется идентификатор – имя метода должно соответствовать роду деятельности, роду выполняемых действий. Если здесь рисуется прямоугольник, то мы что пишем? DrawRectangle, если треугольник то DrawTriangle. Отлично. Значит метод имеет свой идентификатор, набор параметров. Они могут быть или их может и не быть. Возвращаемое значение тоже может быть, а может и не быть. И обязательное тело метода. Тело метода – это блок в операторных скобках, в котором находится определенная полезная функциональность. И мы понимаем, что методы, это такие конструкции, которые мы можем создать один раз, а потом даже не повторно использовать, потому что есть парадигма Повторное использование в ООП. Давайте назовем ее по-своему – многократное использование. Многократно использовать мы можем методы. Многократно использовать в разных частях своего программного кода. Так и переходим уже к самому последнему слайду. Обратите внимание, вы у себя в презентации тоже можете у себя найти. Здесь описывается алгоритм и правила создания методов в первой секции. Во второй секции мы видим, что для вызова метода в нашем коде нужно написать имя метода. Как мы это делали и выполнить все вот эти рекомендации. Замечательно. А мы с вами переходим к практической составляющей нашего урока. Мы заходим в Visual Studio и идем по практической части. Мы начинаем разбирать наши примеры. Анализировать их. И работать с методами. Мы с вами заходим в первый пример и смотрим.
1: using System; 2: 3: // Методы (Процедуры). 4: 5: namespace Methods 6: { 7: class Program 8: { 9: // На 12-й строке, создаем метод с именем Procedure, который ничего не принимает и ничего не возвращает. 10: // В теле метода, на 14-й строке выводим на экран строку - Hello! 11: 12: static void Procedure() 13: { 14: Console.WriteLine("Hello!"); 15: } 16: 17: static void Main() 18: { 19: // В теле метода Main на 21-й строке, вызываем метод Procedure. 20: 21: Procedure(); 22: 23: // Delay. 24: Console.ReadKey(); 25: } 26: } 27: }
На 12 строке в классе Program, мы пока еще не знаем, что такое классы, но мы уже видим, что это некий контейнер, который может содержать в себе пока методы. На 12 строке мы создаем метод с именем Procedure, который ничего не принимает и ничего не возвращает. Давайте посмотрим, вот этот. Мы создаем вот такую разновидность методов. Ничего не принимает, ничего не возвращает. Но мы же знаем, что технически у нас процедур не существует. Почему? Потому что не смотря на то что я говорю что метод ничего не возвращает, он все равно будет возвращать что? Пустоту – void. Давайте еще раз зайдем сюда, посмотрим. F12. Мы видим что это некая пустая структура. Структура – это тоже конструкция языка C#, которая может группировать в себе методы, переменные и много других конструкций. Так вот мы видим, что эта структура в этом случае является пустой. Помните я говорил вам, чтобы вы протянули руку, а я вам – держите ничего. Ну мы будем говорить, что если метод возвращает void – вот такую структуру пустую, то значит он ничего не возвращает. Потому что если я так с вами поступлю, вы скажете что я вам ничего не дал. Еще раз на 12 строке мы с вами создаем метод с именем Procedure, который ничего не принимает, пустые аргументные скобки, и ничего не возвращает. В теле метода мы выводим на экран строку «Hello!» Далее на 17 строке Мы создаем метод Main, который тоже ничего не принимает, ничего не возвращает, помните void – пустота. В теле метода Main на 21 строке мы вызываем метод с 12 строки. Понимаем, что в данном случае что сделает процессор? Он перейдет на 12 строку Выполнит тело и вернется наследующую команду, которая находится за вызовом метода. Правда? И еще раз. На 12 строке мы с вами создаем метод с именем Procedure, который ничего не принимает, и ничего не возвращает. В теле метода мы выводим на экран строку «Hello!» В теле метода Main на 21 строке мы вызываем метод Procedure с 12 строки. Выполнимся? Даже пошагаем. Нажимаем F11. Вот пожалуйста. Куда у нас переход произойдет? На метод. Смотрите, выполнилось, и мы пошли дальше. Обратите внимание, в результате выполнения это программы мы на экране видим строку «Hello!» Значит мы рассмотрели с вами вот такую версию методов, которые ничего не принимают и ничего не возвращают, то есть возвращают пустоту – void, а это нечестно, это не считается. Почему так сделали Microsoft? Об этом мы уже будем говорить на поздних курсах. Уже обсуждать философию языка позже. Сейчас мы должны просто получать удовольствие, и мы с вами должны достичь чего на этом курсе? Легкости восприятия. Чтобы вы не думали, что программирование – это работа. Чтобы для вас это было приятное времяпровождение. Хорошо. Мы идем в следующий пример.
1: using System; 2: 3: // Методы (Функции). 4: 5: namespace Methods 6: { 7: class Program 8: { 9: // На 12-й строке, создаем метод с именем Function, который ничего не принимает и возвращает строковое значение. 10: // В теле метода, используя ключевое слово return, возвращаем строку - Hello! 11: 12: static string Function() 13: { 14: return "Hello!"; 15: } 16: 17: static void Main() 18: { 19: // В теле метода Main на 22-й строке, создаем строковую локальную переменную с именем @string 20: // и присваиваем ей возвращаемое значение метода Function. 21: 22: string @string = Function(); 23: 24: Console.WriteLine(@string); 25: 26: // Delay. 27: Console.ReadKey(); 28: } 29: } 30: }
На 12 строке мы создаем метод с именем Function, там была Procedure – процедура, она ничего не возвращала. Процедуры ничего не возвращают, функции что-то возвращают. В C# не принято проводить такого разделения, такой дифференциации, но мы будем пока это делать, чтобы знать. Значит на 12 строке мы создаем метод с именем Function, который ничего не принимает и возвращает строковое значение. Заметьте, еще поправочка, здесь используем ключевое слово static. Оно нам необходимо в данном случае, чтобы вот в такой простой манере вызывать наши методы, которые мы создаем. В дальнейшем, уже на Essential мы уже познакомимся с философией этого метода. Там будет практически пол урока по static? Но пока нас не затруднить ставить перед именем метода. Еще раз на 12 строке мы создаем метод с именем Function, который ничего не принимает, видите, пустые аргументные скобки. И возвращает строковое значение. В теле метода на 14 строке мы возвращаем строковое значение «Hello!» Смотрите, читается как английский язык – return hello. На 17 строке мы создаем метод Main. Ну не мы его создавали, он был создан по шаблону. Который снова же ничего не принимает и ничего не возвращает. И в теле метода на 22 строке мы создаем строковую локальную переменную. Почему я говорю, что это локальная переменная? Потому что переменные, которые создаются в методах, их принято называть локальными. Потому что мы можем создавать переменные и вот здесь, внутри классов. Но такие переменные мы уже будем называть полями. Мы пока к ним не подходим. И вот такие переменные на языке C# принято называть полями, либо глобальными переменными. Но в языке С++ понятие глобальной переменной – это совсем другое, у нас такого нет, потом будем обсуждать. Но пока мы будем называть все переменные, которые внутри методов локальными. Значит на 22 строке мы создаем строковую локальную переменную с именем @string и присваиваем ей возвращаемое значение function, которое представляет собой строковое значение “Hello”. Поэтому поле выполнения данного метода у нас переменная string будет равна «Hello!» И на 24 строке мы выводим на экран значение строковой локальной переменной @string. Мы говорили, что так не хорошо делать. Ну у нас пример абстрактный, образный. Так, значит какую мы здесь разновидность разобрали? Давайте вернемся к слайду, ничего не принимает и что-то возвращает. То есть это у же была функция. Этот вариант мы рассмотрели. Следующий пример.
1: using System; 2: 3: // Методы (Функции). 4: 5: namespace Methods 6: { 7: class Program 8: { 9: static string Function() 10: { 11: string word = "Hello!"; 12: 13: return word; 14: } 15: 16: static void Main() 17: { 18: string word = Function(); 19: 20: Console.WriteLine(word); 21: 22: // Delay. 23: Console.ReadKey(); 24: } 25: } 26: }
Смотрим, на 9 строке мы создаем метод с именем Function, который ничего не принимает и возвращает строковое значение. В теле метода на 11 строке мы создаем строковую локальную переменную с именем word и присваиваем ей значение «Hello!» И на 13 строке мы возвращаем, используя оператор return. Мы возвращаем значение переменной word. На 18 строке в теле метода Main мы создаем строковую локальную переменную с именем word. Смотрите, они одинаковы, но это ничего не значит. Почему? Потому что это разные области видимости. Отсюда мы не увидим что здесь, а отсюда мы не увидим что здесь. Коллизии нет. Этот word принадлежит методу Function, а другой word – методу Main. Обратите внимание, у нас в разных аудиториях могут находится два человека с одинаковым именем. В одной находится Александр, и в другой находится Александр. Конфликта нет. Правда? Разные области видимости. И еще раз на 18 строке мы создаем строковую локальную переменную с именем word и присваиваем ей возвращаемое значение метода Function, которое представляет собой строковое значение «Hello!» И поэтому после выполнения этого метода переменной word будет присвоено что? Строка «Hello!» И на 20 строке мы выводим на экран значение переменной word. Мы на экран выводим «Hello!» Так, здесь какой у нас метод? Ничего не принимает и возвращает что-то. Опять мы его рассмотрели. Хорошо. Идем дальше. Четвертый пример.
1: using System; 2: 3: // Методы (Функции). 4: 5: namespace Methods 6: { 7: class Program 8: { 9: // На 13-й строке, создаем метод с именем Function, который принимает один строковой аргумент и возвращает строковое значение. 10: // В теле метода, строковой локальной переменной sentence, присваиваем конкатенацию строк и аргумента, 11: // используя ключевое слово return, возвращаем значение переменной sentence. 12: 13: static string Function(string name) 14: { 15: string sentence = "Hello " + name + "!"; 16: 17: return sentence; 18: } 19: 20: static void Main() 21: { 22: // В теле метода Main на 25-й строке, создаем строковую локальную переменную с именем sentence, 23: // присваиваем ей возвращаемое значение метода Function, которому в качестве аргумента передаем строку - Alex. 24: 25: string sentence = Function("Alex"); 26: 27: Console.WriteLine(sentence); 28: 29: // Delay. 30: Console.ReadKey(); 31: } 32: } 33: }
На 13 строке мы создаем метод с именем Function, который принимает один строковой аргумент с именем Name, принимает один строковой аргумент или строковой параметр – это синонимы. С именем Name – это имя параметра, имя аргумента. Зачем? Чтобы мы смогли использовать его внутри. Еще раз на 13 строке мы создаем метод с именем Function, который принимает один строковой аргумент с именем name и возвращает строковое значение. В теле метода на 15 строке мы создаем строковую локальную переменную с именем sentence – переводится как? Предложение. И присваиваем ей конкатенацию двух строк и значения аргумента. На 15 строке мы создаем строковую переменную sentense и присваиваем ей конкатенацию двух строк и аргумента. И на 17 строке мы возвращаем значение sentense. На 25 строке мы создаем строковую локальную переменную string. Они совпадают но не конфликтуют, потому что разные области видимости . На 25 строке мы создаем строковую локальную переменную sentence и присваиваем ей возвращаемое значение метода Function, которому качестве аргумента передаём строковое значение Alex. И метод Function вернет конкатенацию строки Hello аргумента Alex и восклицательного знака. На 27 строке мы выводим: «Hello Alex!» Просто? Просто. Подождите, а мы не посмотрели тип. Тип этого метода. Он принимает параметры и возвращает значение. Смотрите, он и принимает параметры и возвращает значение, то есть относится к функции. Смотрим дальше. Следующий пример.
1: using System; 2: 3: // Методы (Функции). 4: 5: namespace Methods 6: { 7: class Program 8: { 9: ///10: /// Сложение двух целых чисел. 11: /// 12: /// Первое слагаемое 13: /// Второе слагаемое 14: ///Сумма 15: static int Add(int summand1, int summand2) 16: { 17: return summand1 + summand2; 18: } 19: 20: static void Main() 21: { 22: int summand1 = 2, summand2 = 3; 23: 24: int sum = Add(summand1, summand2); 25: 26: Console.WriteLine("{0} + {1} = {2}", summand1, summand2, sum); 27: 28: // Delay. 29: Console.ReadKey(); 30: } 31: } 32: }
Вы у себя тоже держите открытую студию с примерами и пытайтесь что-то изменять. Экспериментируйте, что-то добавляйте, что-то убирайте. Смотрите на ошибки, которые возникают, просто ваша задача сейчас как можно больше работать с кодом, не боятся экспериментировать, никто не выскочит и не укусит. Максимум, может что-то появится в Error List. А я вам говорил, что в нем у нас всегда будет что-то появляться, потому что такого не бывает, чтобы программист программировал и у него не было ошибок. Это нормально. Главное, чтобы мы могли правильно и своевременно, главное своевременно устранять. Смотрите, что мы здесь видим. Мы здесь видим какой-то очень сложный комментарий. Что же это такое? Как его сделать? Показываю. У метода Main такого комментария нет. Его сделать очень просто. Ставим перед методом курсор и делаем тройной слеш. И он сам формируется. Удаляем. Давайте вот здесь удалим. Раз, два, три. Нам осталось только заполнить эти пустые поля, которые нам подготовились. Смотрите, здесь говорится описание метода, что он будет делать. Давайте посмотрим, как мы его заполнили. Он занимается сложением двух целых чисел. Смотрим первое. Что такое первый аргумент? Summand1 – первое слагаемое. Второй – второе слагаемое. Третье – сумма. Вы скажете в чем же здесь удобство. А очень просто. Когда мы описываем такими комментариями я думаю что мы уже все видели MSDN и формат описания конструкций на MSDN, и тех же самых методов, которые мы изучаем. Каждый метод, что-то выполняет. Иногда нам не достаточно для полного понимания одного имени. Не всегда мы можем назвать коротким словом метод так, чтобы он полностью выражал род деятельности этого метода. И потому нас иногда все-таки приходится комментировать, и более того, нам иногда приходится документировать код, потому что программисты не просто сидят и что-то пишут, они еще и большую часть времени занимаются документированием кода. И вот есть такая программа SandCastle – переводится как песочный замок. Она натравливается на наш программный код, выбирает все вот эти комментарии и формирует из них уже готовый красивый документ с описанием частей, методов, классов, с описанием частей наших программ. Поэтому нам иногда удобно использовать тройные комментарии. Конечно же правила рефакторинга говорят, что ваш код должен быть самодокументируемым. Что это значит? Это значит, что мы должны называть все сущности, в том числе переменные, метод, чтобы не писать, что этот метод складывает что-то. Этот аргумент – это первое слагаемое, второе слагаемое. Мы их правильно называем, чтобы мы могли просто правильно его прочитать, Add – сложение. Summand1, summand2 – первое слагаемое, второе слагаемое. Правда? И нам не надо даже читать комментарии, для того чтобы понять что делает этот метод. Но к сожалению такое не всегда происходит. Программисты так говорят не потому что все так делают. Все стараются следовать этому правилу но мы знаем, что законы частенько нарушаются. Почему? Либо по зависящим, либо по независящим от нас причинам. Так же и программном коде. Не всегда работают те идеальные правила, которые рекомендуют нам теоретики компьютерных наук и вот в том числе разработчики программных языков и того же рефакторинга. Хорошо. Значит мы смотрим здесь на 15 строке мы создаем метод с именем Add, который принимает два целочисленных аргумента. С именами summand1 и aummand2. Возвращает целочисленное значение. И в теле метода мы возвращаем сумму аргументов. В теле метода Main на 22 строке мы создаем две локальных целочисленных переменных summand1 и summand2 – это не те же самые, разные области видимости. И присваиваем им значения 2 и 3 соответственно. На 24 строке мы создаем переменную с именем sum и присваиваем ей возвращаемое значение метода Add, которому в качестве аргументов передаем значения переменных summand1 и summand2. В результате метод Add вернет нам что? Сумму значений аргументов. 2+3=5. В результате выполнения это программы переменной sum присвоится возвращаемое значение метода Add – 5. Далее на 26 строке мы уже используя эту конструкцию с форматированием, что мы с вами изучали мы выведем на экран 2+3=5. Просто? Просто. Значит что мы здесь видим? Процедура? Функция? Конечно же функция. Давайте найдем ее. Она и принимает, и возвращает. Они и принимает один или несколько аргументов, и возвращает какое-то значение. Мы постоянно должны себе узнавать. Вот эта табличка показывает все возможные вариации создания методов. Других нет. Переходим к следующему примеру.
1: using System; 2: 3: // Методы (Функции). 4: 5: namespace Methods 6: { 7: class Program 8: { 9: // Методы, которые возвращают логическое значение, называют методами-предикатами. 10: 11: static bool And(bool a, bool b) 12: { 13: return a && b; 14: } 15: 16: static void Main() 17: { 18: bool operand1 = true, operand2 = true; 19: 20: bool result = And(operand1, operand2); 21: 22: Console.WriteLine("{0} && {1} = {2}", operand1, operand2, result); 23: 24: // Delay. 25: Console.ReadKey(); 26: } 27: } 28: }
Смотрим, на 11 строке мы создаем метод с именем And, который принимает два булевых аргумента и возвращает булево значение. Иногда в программировании методы возвращающие булево значение принято еще сленгово называть предикатами. Людей, с которыми мы дружим приято называть друзьями, людей, которые нас учат, принято называть учителями или тренерами. I am your trainer. You my students. Смотрите, на 11 строке мы создаем метода, который принимает два логических аргумента и возвращает логическое значение. Вот такие методы иногда принято называть предикатами. Почему мы не будем вдаваться в эту философию в похожесть, в связь с настоящими предикатами из логики. Вы помните, что мы изучали предикаты, ну кто изучал. Возможно. На 11 строке мы создаем метод с именем And – И, похоже на союз И – конъюнкция, которая принимает два булевых аргумента а и b и возвращает булево значение. И в теле метода на 13 строке мы возвращаем возвращаемое значение оператора конъюнкции, производимое над двумя операндами. Над двумя конъюнктами. Видите, да? А И B. Если а будет равно true, а b будет равно false, что мы получим? False! Помните таблицы истинности. Мы true получим только в одном случае, только когда оба конъюнкта и а, и b будут равны истине. То есть true. Во всех остальных случаях мы получим false. А если я сделаю так: два вертикальных слеша и так оставим… Стоп! Надо что сделать? Переименовать метод. Почему? Имя метода не соответствует логике выполняемых действий этого метода. И как его переименовать? Or. Поэтому и здесь нам придётся переименовать Or. Теперь уже более правильно. Хорошо. И мы заходим в тело метода Main. На 18 строке мы создаем два булевых операнда. Operand1 и operand2. Присваиваем им значения tru и true соответственно. На 20 строке мы булевой переменной result присваиваем возвращаемое значение метода And, которому в качестве аргументов передаем значение операндов operand1 и operand2. Мы видим, сто они равны true и true соответственно. Поэтому метод And очевидно вернет true. Ну и далее мы организовываем вывод, чтобы оттенить работу нашей программы. Хорошо. Мы идем дальше.
1: using System; 2: 3: // Пример правильного множественного возврата из метода. 4: 5: namespace Methods 6: { 7: class Program 8: { 9: static string Compare(int value1, int value2) 10: { 11: if (value1 < value2) 12: { 13: return "Comparison Less Then"; 14: } 15: else if (value1 > value2) 16: { 17: return "Comparison Greater Then"; 18: } 19: 20: return "Comparison Equal"; 21: } 22: 23: static void Main() 24: { 25: int operand1 = 1, operand2 = 2; 26: 27: string result = Compare(operand1, operand2); 28: 29: Console.WriteLine(result); 30: 31: // Delay. 32: Console.ReadKey(); 33: } 34: } 35: }
Обратите внимание, если раньше у нас метод возвращал только одно значение. А у нас имеются сложные условные конструкции и мы можем вернуть либо true, либо false в зависимости от каких-то внутренних условий, которые встречаются внутри метода. Смотрим, на 9 строке мы создаем метод с именем Compare – сравнить, который принимает два интовых аргумента, два целочисленных аргумента: value1 и value2. И метод возвращает строковое значение. В теле метода на 11 строке мы создаем условную конструкцию if, в условии которой указываем выражение – value1
1: using System; 2: 3: // Пример: Использование сторожевого оператора, для защиты номинального варианта. 4: 5: namespace Methods 6: { 7: class Program 8: { 9: static string Function(string name) 10: { 11: // Выполняем проверку. При обнаружении ошибок завершаем работу. 12: 13: if (name == "fool") // Сторожевой оператор. 14: { 15: return "Вы использовали недопустимое слово."; 16: } 17: 18: // Код номинального варианта. 19: 20: string sentence = "Hello " + name + "!"; 21: 22: return sentence; 23: } 24: 25: static void Main() 26: { 27: string sentence = Function("fool"); 28: 29: Console.WriteLine(sentence); 30: 31: // Delay. 32: Console.ReadKey(); 33: } 34: } 35: }
Вы видите, какие полезные и простые конструкции языка – методы. И давайте посмотрим еще один маленький пример. Помните, Дейкстру мы с вами рассматривали. Математик Дейкстра, который ругал goto, который придумал цикл, то есть алгоритм, который назван его именем. Эго алгоритм расширили. Как он называется? Паук. Если забыли – повторите. Давайте посмотрим, здесь тоже нечто похожее. Мы видим здесь какие-то сторожевые операторы, какие-то номинальные варианты. Давайте ее почитаем. Чтобы понять программу нужно ее прочитать. Чтобы понять что нам хотят сказать люди в своем письме, это письмо нужно прочитать. И как вы быстро читаете скорочтением письма, бегло читаете, так и со временем вы научитесь читать программы Вы должны научится постоянно менять фокус. Посмотреть в общем, потом выделить участок. Его прочитать. Не старайтесь все время читать линейно. Не старайтесь. Сразу посмотрели в общем. Вот у нас тут Function, Main, ага понятно. А что делает Function. Так, тут не понятно, а дальше понятно. То есть переводите фокус, работайте с кодом, как в жизни. Вы же не сканируете меня попиксельно, когда заходите в аудиторию, или когда мы с вами встречаемся. Вы смотрите через маленькую щелочку и сканируете меня, а потом досканировали до глаза и забыли, что вы там сканировали выше. Правда? Мы же не сканер. Мы – люди, и вот так же должны смотреть и на программные коды. Тем более вы видите, какие они простые и приятные. На 9 строке мы создаем метод с именем function, который принимает один строковой аргумент с именем name и возвращает строковое значение. В теле метода на 13 строке Мы создаем условную конструкцию if в которой организовываем проверку. Так, что у нас такое fool. Давайте проверим. О, дурачок, глупец. Плохое слово, нельзя такое слово пропускать, правда? И мы должны отфильтровать такие слова из условно нецензурной, ненормативной лексики. Хоть мы их хотим отфильтровать, хоть раз нам придётся их написать. Либо другой способ. Одна девочка говорит, что она не будет писать. Ну тогда остается написать все остальные, правильные слова, которые не входят в словарь ненормативной лексики. Поэтому придется набраться смелости даже тем, кто не употребляет эти слова и его один раз написать с закрытыми глазами. На 13 строке Мы создаем условную конструкцию if в которой проверяем, если значение аргумента равно слову «Дурачок», то мы прекращаем выполнение нашего метода и возвращаем строку «Вы использовали недопустимое слово.» Мы не можем так называть друг друга. А если же здесь идет имя, например, Александр, то на 20 строке мы создаем строковую локальную переменную с именем sentence и присваиваем значение ей конкатенацию двух строк и значение аргумента. «Hello Alexander!» И на 22 строке мы возвращаем уже сформированную строку. Вы видите что мы здесь поставили. Мы здесь поставили некую сторожевую конструкцию, для того, чтобы случайно компьютер мне не сказал «Hello fool!» Но это неприятно, даже когда чат-боты, вы все наверное сталкивались с чат-ботами, интересное направление, нейросети… Когда чат-бот вам отвечает грубо. Вроде вы и понимаете, что это программа, но вы все равно понимаете, что кто-то туже эту логику занес. Кто-то научил его так здороваться. Правда? Если он груб с вами или использует ненормативную лексику, то вам не хочется оставаться в сеансе пользования этой программы. Хорошо. Значит мы видим, что мы должны поставить некие сторожевые операторы, чтобы все таки не пропустить, какие-то возможно неправильные значение в область так называемого номинального варианта. Настоящей чистой логики. Она может оказаться просто незащищенной. Смотрим дальше на 27 строке мы создаем строковую локальную переменную sentense и пытаемся ей присвоить «Привет дурачок!» Мы присваиваем значение ей возвращаемое значение метода Function, которому в качестве аргумента передаем строковое значение fool – дурачок. Что у нас произойдет? Давайте попробуем пошагать. Я честно говоря запутался здесь немного. Давайте попробуем прошагать. Так смотрим. Name = fool - Вы использовали недопустимое слово. Поэтому меня программа обнотифаевает о том, что я использовал неправильное слово. Но если я попытаюсь ввести здесь Alex и сейчас мы пошагаем, я просто хочу проверить как работает эта программа. Подождите секундочку. Не обращайте внимания. Я сейчас быстро проверю и мы продолжим с вами дальше. Name = Alex. Alex не равно fool. Мы обходим сторожевой оператор, он нас пропустил и здесь мы возвращаем конкатенацию Hello, Alex, ! Возвращаем Hello Alex! Мы возвращаем приветствие в строковую переменную и выводим ее на экран. Все вроде бы правильно. Хорошо. Я закончил. Смотрим дальше. Какие у вас есть вопросы? Здесь все очень просто. Пошли дальше. Мы идем дальше по коду. И мы сейчас с вами к еще одной интересной особенности при работе с методами. Но прежде чем рассматривать этот код дальше хотелось бы вернутся в презентацию и внести кое-какие поправки. Не ругайте меня. Здесь этого не показали. Почему? Чтобы не смущать с первого раза. Значит в информатике у методов может быть две разновидности параметров. Это in параметры, которые входят внутрь. Давайте попробуем это как-нибудь пометить. Это во-первых, in параметры. Синее это in. Дальше. Также бывают out параметры. Сейчас нарисуем еще одну стрелочку красную, чтобы было видно. И вот такие параметры, которые выскакивают из метода. Представляете, не только через вот это отверстие могут выходить возвращаемые значения, через return. Но и через вот это отверстие тоже. А мы из пометим с вами как out. Но что делает Microsoft. Она говорит, что мы перед своими параметрами не будем указывать ключевого слова in. И так понятно, что это входящие параметры. Все аргументы, которые мы с вами рассматривали до этого момента они по умолчанию и так были in. Поэтому это слово писать и не обязательно. Но у нас, как мы видим, существует еще одна разновидность параметров – это out. In в английском языке – это в – внутрь, а out – это из. Но и так уже есть один… А я хочу, чтобы мой метод. Больше возвращал значений. Например 2 – строку и число. Или две разные строки, три разных числа. Мне нужно, чтобы из моего метода выходило больше параметров, больше возвращаемых значений. Чувствуете, что для этого нужно делать. Вот это мы с вами и рассмотрим. Еще маленькая поправочка. Что сделали Microsoft? Они сказали, что если в обычной информатике есть такие понятия как int и out, то у нас будет немножко по другому. In – и так понятно. Мы не будем придумывать ключевого слова. А вот out мы сделаем двух типов. Чистый out и второй – это ref. Видите, что у нас получается. Microsoft берет и как-бы расщепляет вот этот out и делает два различных out. И вот чем вот эти out отличаются мы сейчас с вами и посмотрим. То есть главное поймите, что мы можем создавать такие параметры, которые будут являться одновременно и in, мы их сможем здесь использовать. Вот мы их приняли, и еще сможем что-то вернуть. Принимаем его внутрь. Перерабатываем и возвращаем. Видите, вот такая некая петля получается. Замечательно. Думаю, что здесь предельно понятно. In параметры и так идут по умолчанию. А out параметры, мы сейчас до них дойдем. В нашем понятии, out – как собирательное понятие этих всех out параметров, выходных параметров через область аргументов. Microsoft разбивает и на out, и на ref. Это очень просто. Даже не переживайте. Смотрим сейчас в программный код.
1: using System; 2: 3: // Методы с изменяемыми параметрами (ref = in/out). 4: 5: namespace Methods 6: { 7: class Program 8: { 9: // Если в теле метода выполнится изменение значения переменной переданной по ссылке, 10: // ее значение будет изменено везде 11: 12: static int Method(ref int a) 13: { 14: int b = a * 2; 15: a = 5; 16: return b; 17: } 18: 19: static void Main() 20: { 21: int operand = 2; 22: 23: int result = Method(ref operand); 24: 25: Console.WriteLine("{0}; {1};", operand, result); 26: 27: // Delay. 28: Console.ReadKey(); 29: } 30: } 31: }
На 12 строке мы создаем метод с именем Method, который принимает один целочисленный аргумент с именем а, помеченный параметром ref. Давайте сейчас смотреть как это работает, и возвращает целочисленное значение. В теле метода мы создаем локальную переменную b, и присваиваем ей произведение аргумента а на 2. Далее, на 15 строке мы меняем значение этого аргумента. Мы присвоили ему значение 5. И на 16 строке мы возвращаем значение b. Казалось бы все просто. Смотрим, на 21 строке мы создаем локальную переменную operand и присваиваем ей значение 2. На 23 строке мы переменной result присваиваем возвращаемое значение метода Method, при этом в качестве аргумента передаем ему этот операнд. То есть мы передвем что? Двоечку. Значит двойка попадает сюда. Мы здесь видим, на 14 строке мы b присваиваем 2*2=4. На 15 строке мы а заменяем. Она была равна чему? Двойке. И возвращаем значение b. Хорошо. Лучше всего – пошагать. Давайте пошагаем. Смотрим. Operand = 2. Идем на метод. А = 2. Так заходим, смотрим. Если а равна 2. Operand чему равен? 2. А = 5. Возвращаем 4. А уже равна 5. Operand – пока еще неизвестно. Интересно, поменяет метод его значение, или не поменяет. Смотрите, operand стал равен 5. Представляете, вот этому методу удалось изменить, удалось каким-то образом побежать в другой метод и изменить его значение. Благодаря чему? Благодаря тому, что у нас здесь используется это ключевое слово ref. И теперь у нас произошел вот такой эффект. Вы скажете, что будет если убрать ключевое слово ref. Хорошо, мы сейчас попробуем убрать его. Но мы сначала довыполним программу. Видите? Операнд стал равен 5. А result стал равен 4. Видим, что сюда как раз и подставился этот операнд вместо 0. А в 1 у нас подставился result. Видите, что произошло? Хорошо. Давайте попробуем теперь взять и удалить ref. Что же у нас сейчас произойдет? Выполняемся. Мы не можем. Мы забыли вот где удалить. Смотрите, ref используется парно, как в самом методе, так и при его вызове. Например описании аргументов метода, так и при вызове этого метода. Сейчас сработало. Но что мы видим? Мы видим, что operand как был 2, он так и остался равен двум. Методу не удалось изменить значение вот этого внешнего операнда, который передавался в качестве аргумента метода. Вы скажете – как так получилось? Очень просто. Когда мы используем методы без ref, у нас сюда в качестве аргумента метода на самом деле передается не сам аргумент. А что там? А здесь просто его копия. Копия этого операнда. То есть, когда мы передаем в качестве аргументов методов значение каких-то переменных, то не сама переменная сюда передается, а копия значения это переменной. Но если мы хотим отказаться от такого похода, от передачи копий, что нам нужно сделать? Указать ключевое слово ref перед аргументом, но будьте осторожны. Вы видите к какому эффекту это может привести? Это может привести к тому, что изменив значение такого аргумента у себя в теле метода вы поменяете значение какой-то чужой переменной, которая будет передаваться при вызове вашего метода в качестве аргумента. ref отказывается от использования копий передаваемых параметров. Просто? Просто. Если указываем ref, то мы начинаем работать с самим оригиналом. По умолчанию это закрыто, потому что представляете сколько было бы ошибок. Мы передаем, а эта переменная меняется, а мы не хотели бы что бы она менялась. Вы видите что происходит здесь? Значит ключевое слово ref позволяет нам отменить передачу копии значения, оно говорит, что мы будем работать с оригиналом. И поэтому вы видите? Двойное использование. Double using. Как здесь используется ключевое слово ref, так и здесь. Работал какой-то другой программист, который дал вам этот метод. А вот здесь работаете вы. И мы с вами помним, что если мы забудем указать это ключевое слово, то на подчеркнется. Ошибка. Как? А здесь же используется ref. А зачем меня заставляют сюда передавать такое значение, которое нужно поменять. Это же не безопасно. Что это за логика, такая интересная. Я тут храню какие-то свои значения в операнде, а тут мне его собираются изменить. Но в каких-то случаях это требуется для быстроты и для оптимизации. Думаю, что здесь предельно понятно. Но мы с вами помним, что у нас в информатике есть два вида параметров: in и out. Если мы напишем in? то не смотрите, что оно загорелось синим, в языке C# есть такое ключевое слово, но оно не совместимо. Вот во многих языках, например в языке АДА его нужно писать. АДА. Августа Лавлейс первая женщина-программист, которая писала программы для Чарльза Бэббиджа под его несуществующие машины. И мы видим, что в языке АДА нужно указывать такое слово. В языке C# - не нужно. Мы и так подразумеваем, что оно здесь используется если мы не указываем ref или out. Потому что мы уже смотрели уже, что понятие out оно разделяется. И теперь мы видим, что происходит, когда мы используем ref. То практически у нас получается некий возвращаемый параметр. Вот из тела этого метода происходит присвоение значения вот сюда. Чем то напоминает работу return. Только return просто, как из рогатки, выстреливает, а здесь целенаправлено изменится значение переменной, которая будет передана в качестве аргумента метода. Видите, если этот аргумент буде помечен спецификатором ref. Видите да? А мы с вами переходим дальше.
1: using System; 2: 3: // Методы с выходными параметрами (out). 4: 5: namespace Methods 6: { 7: class Program 8: { 9: static int Method(out int a) 10: { 11: // Выходные параметры должны быть изменены в теле метода, иначе будет ошибка. 12: a = 2; 13: return a * 2; 14: } 15: 16: static void Main() 17: { 18: int operand; 19: 20: // out - позволяет передавать в метод непроинициализированные переменные. 21: 22: int result = Method(out operand); 23: 24: Console.WriteLine("{0}; {1};", operand, result); 25: 26: // Delay. 27: Console.ReadKey(); 28: } 29: } 30: }
В этом примере мы видим использование еще одного ключевого слова out. Значит мы помним, у нас имеется ref и out. То есть стандартный классический out разделается на ref и out. Давайте почитаем эту программу. На 9 строке мы создаем метод с именем Method, который принимает один целочисленный аргумент, помеченный спецификатором out. Этот метод возвращает целочисленное значение. Обратите внимание, в теле этого метода на 12 строке, мы этому аргументу присваиваем значение 2. И на 13 строке мы возвращаемое произведение этого аргумента и 2. На 18 строке мы создаем локальную целочисленную переменную operand. Мы ее ничем не инициализируем и на чтение мы ее использовать не можем. На 22 строке мы создаем целочисленную локальную переменную result, которой присваиваем возвращаемое значение метода и в качестве аргумента этого метода мы передаем вот эту переменную. Мы же понимаем, что ref\out откажется от построения копий. А тем более здесь и значения никакого нет. Дальше мы заходим в наш метод. Здесь мы ей присваиваем 2. Этот операнд, эта переменная будет сразу равна 2, потому что ей вот здесь присваивается 2. Как? Через вот этот параметр. То есть мы обращаемся вот сюда. Затем идет вызов метода и мы вот сюда ее записываем. Чем же отличается out от ref. Out – это более строгая форма ref, потому что если бы мы здесь указали ref… Давайте просто поменяем и посмотрим. У нас все равно будет ошибка. Мы пытаемся передать непроинициализированную переменную. Уже не работает. Потому что для того что бы все сработало нам нужно присвоить этой переменной значение. Все, теперь работает. Далее. Если мы закомментируем вот здесь и ничего не будем изменять. Выполняемся. У нас все сработало. При этом не смотря на то что мы 2*2 она осталась 2, потому что данный оператор не меняет значения аргумента. То есть вот все сработало. Но если мы с вами откажемся от ref и вернемся к out. Мы видим что во-первых out позволяет использовать непроинициализированные переменные в качестве аргументов, потому что мы все равно гарантированно вот здесь ей что-то присвоим. А давайте попробуем ей ничего не присваивать. Что мы видим? Ошибку. Сразу же появилась ошибка – непроинициализированная переменная. Давайте здесь 2 присвоим. Все равно не хочет выполнятся. Смотрите, что написано. The out parameter 'a' must be assigned to before control leaves the current method. Он обязательно должен быть внутри метода как-то проинициализирован. Пусть повторно, но должно быть присвоено значение. Не смотря на то, что сюда поступает нормальная, обычная 2. Это не то что здесь непроинициализированно. Использование оператора out накладывает на нас такие ограничения, как обязательное присвоение аргументу какого-то значения внутри метода, аргументы которого помечены спецификатором out. Какое различие между ref и out? Строгость. В первую очередь это строгость. Если ref позволяет необязательно присваивать. Я могу как изменить, так и не изменять. То с out меня обязывают изменять. Потому что когда я использую ваш метод, я ставлю скобку и мне сразу показывается out. Я понимаю, что это возвращаемое значение через блок аргументов, что сюда, то что я передам в итоге оно перезапишется, потому что метод что то мне хочет выдать. А зачем такое сделано? Когда мы раньше работали с API функциями, то там очень часто использовался такой подход с передачей возвращаемых значений через аргументы. Почему? Потому что функции имели очень много параметров и все API функции возвращали нам успех, либо не успех выполнения этой функции. API – Application Programming Interface. Это просто набор уже готовых функций, которые находятся в библиотеках. Возможно вы встречали такие библиотеки на своем диске. Это kernel32.dll, user32.dll. Вот там где находятся как раз API функции, которые и используются для работы с ОС Windows. И каждая из этих функций должна была нам возвращать успешно ли она выполнилась, все ли хорошо сработало. Либо не успешно. А возвращаемые значения мы очень часто получали как раз именно через параметры. Поэтому возвращаемое значение как мы понимаем может быть не одно, не два. Представьте что мы возвращаем координаты разные. Нам предлагается вернуть либо структуру с этими координатами. Но все равно такой подход остался. Может быть он не совсем идеальный и не совсем удобный. Но он остался и его нужно знать. И сегодня тоже используется такой подход в библиотеках Microsoft, потому что мы знаем, что библиотеки Microsoft предоставляют нам очень много готовой функциональности, она вся сконцентрирована в специальных программный библиотеках, которые установлены у вас на компьютере. Эти библиотеки еще называются знакомым нам именем – фреймворк – каркас. Но мы не будем сейчас заострять внимание на непонятных вещах. Мы сейчас изучаем только основы. А к этому мы подойдем немножко позже. А мы с вами переходим дальше и смотрим следующий пример.
1: using System; 2: 3: // Метод с выходными параметрами. 4: 5: namespace Methods 6: { 7: class Program 8: { 9: // Выходные параметры должны быть изменены в теле метода, иначе будет ошибка. 10: 11: static int Method(out int a) 12: { 13: // Закомментировать! 14: a = 1; 15: 16: return 2; 17: } 18: 19: static void Main() 20: { 21: int operand; 22: 23: // out - позволяет передавать в метод непроинициализированные переменные. 24: 25: int result = Method(out operand); 26: 27: Console.WriteLine("{0}; {1};", operand, result); 28: 29: // Delay. 30: Console.ReadKey(); 31: } 32: } 33: }
На 11 строке мы создаем метод с именем Method, который принимает один аргумент с именем а. И при чем он помечен спецификатором out. Почему я говорю спецификатором. Многие могут сказать – модификатором. Но модификатор должен что-то модифицировать, изменять. Модно назвать его модификатором. Но в каких-то случаях такие конструкции называют спецификаторами. В теле метода Method мы на 14 строке аргументу а присваиваем значение 1. И на 16 возвращаем 2. Здесь нам предлагается закомментировать. Как вы думаете, если я закомментирую? Уже будет ошибка, потому что у нас имеется некий constraint. Некое ограничение при работе с out параметрами. Потому что если бы здесь был ref, я программисту говорю что бы он был осторожен возможно я что-то передам. Но если я использую out, я говорю, что программист, я обязательно здесь что-то передам. Если я вдруг забуду это сделать. То компилятор мне напомнит. The out parameter 'a' must be assigned to before control leaves the current method. Он говорит, что обязательно должен передавать. И я сразу знаю, что что-то я здесь забыл. Ага я забыл сработать с этим параметром. И на 21 строке мы создаем переменную operand типа int. Заметьте, при использовании out мы можем себе позволить оставить эту переменную непроинициализированной. Конечно, это изначально плохой подход – оставлять локальные переменные непроинициализированными, потому что им по умолчанию не присваивается значение по умолчанию. Автоматически им ничего не присваивается, потому что если бы эта переменная находилась вот здесь, внутри класса. Видите, она находится в области классов за пределами методов. Она называлась бы полем и ей автоматом присвоилось бы значение по умолчанию – 0. Но для локальный переменных такого не происходит. Поэтому не оставляйте непроинициализированные переменные. Это может привести к временным неприятным эффектам вот время разработки. Это будет вас раздражать. Это же не сложно взять и поставить 0. Ну уже о правилах разработки, о правилах правильного написания кода вы будете смотреть и изучать на курсе рефакторинга. Что это такое вы можете посмотреть первые пробные уроки и соответственно узнать хотя бы основы. Что это такое. На 25 строке мы создаем целочисленную переменную result и присваиваем ей возвращаемое значение метода Method. И после выполнения чему же будет равен этот операнд. Шагать будем? Нет уже не будем. Он будет равен 1. Operand = 1, а result? 2. А мы переходим дальше.
1: using System; 2: 3: namespace Methods 4: { 5: class Program 6: { 7: // Параметр, передаваемый по значению, уничтожается при возврате значения методом 8: static void AddTwo(int a) 9: { 10: a = a + 2; 11: Console.WriteLine("Значение int a = {0}", a); 12: } 13: 14: static void Main() 15: { 16: Console.WriteLine("Введите число:"); 17: 18: // Принимаем ввод от пользователя и преобразуем его в целочисленное значение 19: 20: string number = Console.ReadLine(); 21: int result = Int32.Parse(number); 22: 23: Console.WriteLine("Значение result = {0}", result); 24: 25: // В качестве аргумента передается не сама переменная - result, а её копия. 26: AddTwo(result); 27: Console.WriteLine("Значение result = {0}", result); 28: 29: // Delay. 30: Console.ReadKey(); 31: } 32: } 33: }
Смотрим. Что происходит здесь? На 8 строке мы создаем метод с именем AddTwo, который принимает один целочисленный аргумент и ничего не возвращает. Обратите внимание, мы в этом методе параметру а присваиваем сумму этого параметра и двух. Метод и называется – прибавить двойку. Мы к этому параметру всякий раз будем прибавлять двойку в любом месте, где мы вызовем этот метод. И выводим на экран значение а. Чему оно будет равно? 2. На 20 строке мы предлагаем пользователю ввести некое число. На 21 строке мы преобразовываем число. Помните мы увеличивали число. Говорили, что любое число состоит из точек. Это просто картинка. Я не могу сложить два альбомных листа, на которых написано один, а на другом 2. Это что? 1+2 = 12. Нет! Мне нужно переконвертировать это во что? В нейро-код, и там как-то выполнить это. А мы здесь делаем то же самое. Мы берем строку. Мы ожидаем, что пользователь введет действительное число, а не какую-то абрукадабру. Мы здесь уже не ставим защиту от неправильного ввода. Ее еще называют защитой от дурака. Значит на 21 строке мы конвертируем строковое представление числа в целочисленный формат. Видите мы используем Int32. На нем Parse – преобразовать. И он его преобразовывает и возвращает преобразованное из строки число в целочисленный формат. На 23 строке мы его выводим. На 26 строке мы вызываем метод AddTwo И в качестве аргумента передаем значение переменной result. И смотрим на 27 строке выводим его значение. Обратите внимание, что сюда передается? Сюда передается не сам аргумент, а его копия. Сюда оно копируется. Если я введу двойку, то она попадает сюда и мы ведем работу с копией. Я думаю, вы это запомнили. Этот пример, он в другой форме повторяет, все сказанное нами ранее. Почему? Потому что многие программисты могут забыть о существовании или о правилах использования таких ключевых слов, спецификаторов, модификаторов как ref и out. А сейчас по умолчанию какой параметр? In. А почему вы не пишите? А в языке C# не надо писать ключевое слово in. Мы и так понимаем, что это только для входа. А если мы здесь напишем ref, то он будет и in, они все будут in, они все входят, но и будут обладать особенностями out. Ref – это необязательный out. Программист! Просто будь осторожен, если я здесь ставлю ref. Возможно я поменяю значение твоей переменной напрямую, которую ты передаешь сюда. А если же я поставлю out, то я говорю – Программист! Обязательно мне что-то передай, обязательно. Потому что это настоящее возвращаемое значение через этот блок аргументов. Хорошо. А мы переходим дальше. Заходим в следующий. Что ми видим здесь? Мы видим тот же самый код, что и в предыдущем примере.
1: using System; 2: 3: namespace Methods 4: { 5: class Program 6: { 7: // Передается ссылка на исходные аргументы копия не создается. 8: static void AddTwo(ref int a) 9: { 10: a = a + 2; 11: Console.WriteLine("Значение int a = {0}", a); 12: } 13: 14: static void Main() 15: { 16: Console.WriteLine("Введите число:"); 17: 18: // Принимаем ввод от пользователя и преобразуем его в целочисленное значение 19: 20: string number = Console.ReadLine(); 21: int result = Int32.Parse(number); 22: 23: Console.WriteLine("Значение result = {0}", result); 24: 25: // В качестве аргумента передается сама переменная - result, а не её копия. 26: AddTwo(ref result); 27: Console.WriteLine("Значение result = {0}", result); 28: 29: // Delay. 30: Console.ReadKey(); 31: } 32: } 33: }
Только что изменилось? Здесь в одном месте добавилось ref. Нет не в одном. Ref и out используются парно. Давайте посмотрим где же его пара? Вот идет пара. А зачем парно? Затем, что мы знаем, что частенько методы и классы разрабатывают одни программисты, а используют другие. И это для того, чтобы когда программист-пользователь забыл поставить это ключевое слово, и тут произойдет изменение этого result. Он будет долго анализировать свою программу и не понимать как же оно так получилось. Придется ему долго шагать. Промышленные программы они не такие простые, как мы рассматриваем, потому там может уйти очень много времени на отладку программы на ее анализ и поиск ошибок. Поэтому, если программист пользователь этого метода забудет указать ключевое слово ref – ему будет ошибка. Ага, мы забыли указать ключевое слово ref. И когда я это вижу, тогда разработчик метода, его даже не видно, он где-то там в библиотеках, значит он мне говорит, чтобы я был осторожным, может случится так, что я поменяю значение этой переменной, оно изменится, потому что я получаю к ней доступ напрямую, я работаю не с копией. Я тогда открываю и начинаю смотреть что же делает этот метод. Читать документацию. Чужой код нам редко приходится читать, это считается плохим стилем, когда начинаем читать чужой код. Нам тогда на работе говорят, что мы на работу приходим что делать? Работать, а не учится. И тем более перечитывать повторно код за другими программистами. Для этого есть тестировщики, которые находят ошибки. Если вы считаете что код работает некорректно обратитесь в отдел тестирования и скажите, что вот этот метод мне неадекватный результат выдает, либо я не могу научится им пользоваться. И уже в отделе тестирования скажут либо так, либо так. В отделе качества. Хорошо. А мы с вами продолжаем. Мы все с вами обыгрываем. Почему мы так остановились на ref и out. Потому что он используется не так часто, метко но редко. Нам важно запомнить. А что это за пример?
1: using System; 2: 3: namespace Methods 4: { 5: class Program 6: { 7: static void Add(ref int x, ref int y, int sum) 8: { 9: sum = x + y; 10: } 11: 12: static void Main() 13: { 14: Console.WriteLine("Введите 1-e число."); 15: 16: string operand1 = Console.ReadLine(); 17: int summand1 = Int32.Parse(operand1); 18: 19: Console.WriteLine("Введите 2-e число."); 20: 21: string operand2 = Console.ReadLine(); 22: int summand2 = Int32.Parse(operand2); 23: 24: int sum = 0; 25: 26: Add(ref summand1, ref summand2, sum); 27: 28: Console.WriteLine("{0} + {1} = {2}", summand1, summand2, sum); // Ошибка. 29: 30: // Delay. 31: Console.ReadKey(); 32: } 33: } 34: }
На 7 строке мы создаем метод с именем Add, который принимает три целочисленный аргумента х, у, sum. Смотрим, Первый помечен ref, второй тоже, третий ничем не помечен. На 9 строке мы аргументу sum присваиваем сумму двух первых аргументов. Здесь нам предлагается ввести первое число. Давайте попробуем выполнится. Проверить как она работает. Я говорю 2, второе число – 3. Это должно быть 5. Так, подождите, почему 0. Вот мы и попали с вами в ошибку. Теперь нам с вами придется сидеть и перечитывать код. Давайте перечитывать и вникать в чем же здесь проблема. Метод у нас не сработал, работает некорректно, приходится изучать коды. Так, ввели первое число, конвертировали. Ввели второе число. Конвертировали тоже нормально. Вызываем метод Add, так передаем первый summand1, передаем второй summand2, что мы ввели. Передаем sum. Здесь логическая ошибка. Что нудно сделать для корректной работы этой программы. Вы скажете, что программист напихал ref вот сюда, хотя они здесь сто лет не нужны, мы не собираемся менять операнды, забыл поставить ref вот сюда, раз он уже выбрал такой подход для работы. Убираем здесь ref, убираем здесь ref, нужно еще сюда ref поставить, потому что эти модификаторы используются парно. Выполняемся и проверяем. Вводим 2 и 3. Наконец-то все сработало. Но, обратите внимание, смесь двух логик. Эту логику делал один программист. А другую логику, тот что использовал первую. Получается как. То что мы с вами изменили, мы влезли в чужой код. Этого делать нельзя. Возвращаемся. Получается что кто здесь неправильно создал программу? Этот неправильно воспользовался ею, а это неправильно создал. Уже сейчас учитесь находить ошибки неправильного стиля. Это специально сделано, что мы могли проанализировать, ну зачем мы помечаем ref эти два аргумента? Неужели мы собираемся менять значение операндов, которые вводит пользователь? Нет! Ну допустим, да мы хотим вернуть сумму. Идеально было бы сделать вот так, организовать как возвращаемое значение метода Add и здесь указать return. Ну видите? Теперь что вам нужно сделать? Вам нужно дома попробовать после урока сесть и привести эту программу к красивому виду, как бы вы это сделали и проанализируйте все некрасивые моменты этого кода. А мы переходим дальше в последний пример.
1: using System; 2: 3: namespace Methods 4: { 5: class Program 6: { 7: static void Add(ref int x, ref int y, out int sum) 8: { 9: sum = x + y; 10: } 11: 12: static void Main() 13: { 14: Console.WriteLine("Введите 1-e число."); 15: 16: string operand1 = Console.ReadLine(); 17: int summand1 = Int32.Parse(operand1); 18: 19: Console.WriteLine("Введите 2-e число."); 20: 21: string operand2 = Console.ReadLine(); 22: int summand2 = Int32.Parse(operand2); 23: 24: int sum; 25: 26: Add(ref summand1, ref summand2, out sum); 27: 28: Console.WriteLine("{0} + {1} = {2}", summand1, summand2, sum); 29: 30: // Delay. 31: Console.ReadKey(); 32: } 33: } 34: }
Почему мы показываем код не совсем корректный, потому что вы часто будете встречаться с такими кодами, потому что множество разных людей, у каждого свой уровень компетенции, у каждого свой уровень квалификации, компетенции. Если человек пишет не совсем красивый код с точки зрения общепринятых правил, ну ничего страшного, может он еще не доучился. Мы видим, что он учится, он старается, он над собой работает. И иногда нужно подсказывать, помогать и не совсем так строго судить. Мы смотрим дальше на 7 строке мы создаем метод Add и вот здесь мы указываем out и вот здесь я вижу уже более правильно, но что вам не нравится. Вы скажете, что зачем вам сюда, зачем summand помечаются как ref. Вот снова у нас идет анализ этого кода. А вот так уже это более красиво. И здесь мы могли бы вернуть успешность либо неуспешность выполнения данной операции. Выполняем какой-то сложный расчет, можем даже провести маленький тестик и сказать, что да действительно, тест прошел успешно и вернуть значение true или false в зависимости от того, как выполнился этот метод. Но мы уже не используем таких подходов с возвращаемыми значениями успешного, либо не успешного результата операции. Это раньше так было. Это было вынужденное решение, потому что были сотни и тысячи методов в библиотеках, это были очень низкоуровневые методы и с ними было работать не сложно, запутанно. Были очень толстые книги, которые описывали работу этих методов. Еще не были так развиты интернет ресурсы, как сегодня. Нельзя было быстро и легко найти информацию. Приходилось покупать много литературы. Делится ей друг с другом и делится друг с другом информацией в живую. Не то что не было интернета. Он был. Не было столько ресурсов, не было столько полезной информации как сегодня. Хорошо. И мы с вами закончили рассматривать методы. Мы достаточно много уделили времени рассмотрению этих конструкций. Давайте еще раз зайдем напоследок и посмотрим вот этот слайд. Запомните его. Какие у нас имеются разновидности методов, формы использования методов. Вы уже видите вот эти стрелочки вверх. Давайте я их здесь напоследок пририсую. Вот эти стрелочки, которые говорят о том, что параметры могут быть как входные, так и входно-выходные. Он одновременно и входит, и выходит. И выходные параметры бывают необязательны и обязательны. На этом наш урок закончен, спасибо за внимание. До новых встреч.