- •28: Console.ReadKey();
- •29: Static void Main()
- •36: // Delay.
- •37: Console.ReadKey();
- •34: // Delay.
- •35: Console.ReadKey();
- •1: Using System;
- •1: Using System;
- •9: Namespace Methods
- •11: Class Program
- •28: // Delay.
- •29: Console.ReadKey();
- •1: Using System;
- •8: Namespace Methods
- •10: Class Program
- •24: Static void Main()
- •28: // Delay.
- •29: Console.ReadKey();
- •1: Using System;
- •7: Namespace Methods
- •9: Class Program
- •32: Static void Main()
- •36: // Delay.
- •37: Console.ReadKey();
1: Using System;
2:
3: // Возврат значений из метода Main ()
4:
5: // Перегрузка метода Main () - допустима.
6:
7: // Точкой входа в программу может быть метод Main (), который возвращает значение типа void или int.
8:
9: Namespace Methods
10: {
11: Class Program
12: {
13:
14: // Перегрузка. (Не является точкой входа)
15: static string Main(string argument)
16: {
17: return "Hello " + argument + "!";
18: }
19:
20:
21: // Точка входа в программу.
22: static int Main()
23: {
24: string @string = Main("World");
25:
26: Console.WriteLine(@string);
27:
28: // Delay.
29: Console.ReadKey();
30:
31: return 0;
32: }
33: }
34: }
Мы видим, что у нас имеется Main с возвращаемым значением string. CLR скажет, что не будет этот брать. Ей нужно чтобы у Main был либо int, либо void. Давайте попробуем еще один. Static void Main() {} Сделали. Смотрим. У нас теперь 2 метода. Выполняем. Type 'Methods.Program' already defines a member called ‘Main’ with the same parameter type. Параметры не нравятся. Хорошо, давайте попробуем сделать какой-нибудь параметр. Int a. Почему бы и нет. Билдимся… Успешно. Как вы думаете что у нас выполнится. Скорее всего вызовется вот этот метод. Давайте проверим. Берем и выводим на экран сообщение, например «void Main». Выполняемся. Смотрите, вывелся Hello World! Вот этот метод, и вот этот не вызвались. Почему? До этого мы видели, мы еще с вами массивы не проходили, но у нас был метод с массивом строковых аргументов была такая конструкция. Мы сейчас ее напишем быстро. Был вот такой метод. Давайте попробуем сейчас выполнится. Появилась ошибка. Вот мы и подловили нашу CLR. Вот, проблемы с Entry Point – это точка входа. Compile with /main to specify the type that contains the entry point. Есть только два метода Main, которые могут содержать точку входа. Обратите внимание, хитрость, массив строковых аргументов. Мы к этому массиву подойдем попозже. Но теперь мы видим, что имеется две точки входа в систему и для программы теперь это коллизия. Он говорит, что вы можете оформить только вот такой метод, либо вот такой метод. Вот это не является точкой входа в программу. У нас появилось две точки входа. А не возможно сразу начать выполнять программу с двух мест. Поэтому мы должны отдать предпочтение либо этой точке входа, либо этой точке входа. Но это мы сами уже тут накуролесили. Давайте закомментируем этот блок. Выполняемся. Void Main. Сработала вот эта точка входа. Закомментируем вот эту. Выполняемся. Выполнился Hello World! Выполнился другой метод Main. Поэтому просто запомните себе что метод Main в большинстве случаев является точкой входа в программу. Да, мы можем делать определенные перегрузки этого метода, но зачем. Мы понимаем, что имя метода должно отражать род выполняемой им деятельности. Я не знаю что можно мне придумать даже в маленькой программке, чтобы у меня было два метода Main тем более две точки входа. Они недопустимы. Ну и мы понимаем, что точки входа могут отличатся чем? Методом Main и его возвращаемыми значениями. А если вот здесь поставить массив строковых аргументов? Давайте попробуем и вот здесь тоже поставим массив строковых аргументов, потому что и так система не хочет различить их. И так, что с точки зрения перегрузок это разные методы, но по какой части метода исполняющая среда определяет перегрузку метода? По возвращаемому значению. Вот по чему идет перегрузка. Вот это не учитывается. Мы можем сюда подставить. Система определяет перегрузку метода Main, точки входа по возвращаемому значению. Но это немного сложно и запутанно. Но может вы повторно прослушаете еще раз этот эпизод с этими примерами и возможно у вас будет больше понимания. У вас есть возможность несколько раз прослушивать эти примеры. Хорошо. Мы здесь тогда закомментируем, и вы себе закомментируйте, пусть оно останется. У меня все равно тестовый пример и я его после курса удалю. Выполняемся и смотрим. Hello World! Переходим к следующему примеру.
И следующая важная техника, которая используется при работе с методами – это рекурсия. Что же такое рекурсия? Скажу одним словом – самовызов. Когда метод из своего тела вызывает сам себя. Когда метод обращается сам к себе по имени. Вот представьте, что я сижу и сам себя зову. Рекурсивный вызов. Я сам себя вызвал. Не вы меня зовете, а я сам себя. Самовызов. И мы сейчас с вами разберем как эта техника работает, у нее есть свои определенные особенности. Давайте посмотрим сначала на слайд. Обратите внимание, у нас имеется метод с именем Recursion. В теле метода Main мы вызываем этот метод, и передаем ему параметр 2. Мы понимаем, что counter теперь равен 2. В теле метода мы уменьшаем значение аргумента на 1. Выводим на экран counter. Вот мы вывели единицу на экран. Далее мы проверяем в условной конструкции counter != 0. Условие удовлетворяет истинности и мы входим в тело условной конструкции и еще раз пытаемся вызвать себя, то есть еще раз вызывает себя, но уже в качестве аргумента мы уже передаем единицу. Counter = 1. Что происходит в этот момент. Внимание эффект. У нас в памяти выделается область. Здесь система скопипастила и вставила такой же самый метод. Обратите внимание, мы переходим теперь куда? На копию. Мы вот здесь прыгаем сюда на копию. Мы видим, что counter чему при вызове равен был? 1. Уже работаем с копией. Мы отсюда уже ушли. Мы вот здесь остановились. Тут еще кусок программы. Подождите. Работаем с копией. Counter = 1. Уменьшаем его на 0 и теперь он будет равен 0. На оригинале он остается равным единице. Видите? А в копии counter чему равен? 0! Два одновременно существующих блока в памяти. Оригинал и копия. Здесь counter остается равным 1. Здесь он равен 0. Мы выводим на экран 0. Далее, проверяем в условной конструкции if counter != 0. False. И мы что? Мы обходим ее, не заходим. Мы в нее не зашли. И что мы видим? Снова же на экран. Counter чему равен в этой копии. 0. Смотрите, мы выводим второй ноль. Мы завершили работу метода. Только мы завершили работу метода, мы сразу переходим на следующую команду после вызова этого метода. Мы вернулись в оригинал из копии. Копии нет. Она свое дело сделала. Смотрим, а чем здесь равен counter в оригинале? 1! И мы выводим на экран 1. Обратите внимание, как работает рекурсия. Самовызов. Во время рекурсивного вызова, когда мы внутри имеющегося метода, имеющейся функции обращаемся к своему же имени. Вызываем себя. Что у нас происходит? Происходит построение копии этого метода и переход на эту копию. При этом оригинал. Остается временно замороженным. С ним ничего не происходит, все его переменные остались такими, какие они есть. Вы скажете: «Интересно, а как же метод узнал… Ладно мы перешли сюда, выполнили его и вернулись обратно вот сюда. Как же вот это этому методу стало известно, что надо перейти именно вот сюда?» А дело в том, что каждый раз, после перехода на новый метод мы в специальной области памяти, которая называется стеком, сохраняем адреса возврата. Адреса возврата из вызванных нами методов. Мы знаем, что каждая инструкция имеет свой адрес, просто она скрыта и мы ее не видим. Значит стек – это область памяти для хранения адресов возврата из процедур. Скажу что наша программа строится примерно вот так. Любая программа строится по вот такому принципу. Сначала идут все данные в программе. Дальше идет блок с кодом, а дальше идет стек. Область стека. Потому что здесь идет вся работа с данными. Здесь находятся коды. Здесь изменяющиеся данные. А вот здесь стек, в котором учитывается в первую очередь адреса возврата из вызванных методов. Потому что мы можем из одного метода вызвать другой, потом еще один вызвать, потом еще… У нас такие длинные цепочки методов, а потом мы довыполнились, идем дальше по программе, вызываем еще методы. И вот эти вызовы методов контролируются, то есть мы контролируем возврат. Что нам напоминает стек в жизни. Представьте себе глубокое подземелье. Много этажей, как гиперкуб, например. Много этажей и много комнат. Представьте, что там имеется миллион комнат. И в этих катакомбах, в этом сложном лабиринте есть вход. Все комнаты пронумерованы. Представьте себе. Каждая комната имеет табличку с номером. Там еще много этажей и мы вас берем и говорим: «Так, мы вам сейчас отправляем в лабиринт искать золото, там лежат клады золота в каких-то комнатах, мы не знаем в каких. Что вам з собой нужно?» Вы скажете, что вам нужен рюкзак, чтобы бросить туда тушёнки, воды, путь достаточно долгий, фонарик, свечки, спички. А главное что? Чтобы не заблудится. Это же лабиринт, и люди могут заблудится в лабиринтах. Мы и так здесь вам помогли. Мы дали лабиринт из изначально пронумерованными комнатами и с каждой комнаты по несколько входов и выходов есть. Веревка? Во-первых, если ее возьмёте с собой, такой моток веревки – это очень тяжело будет. Если веревка станется здесь, вы не потянете эту катушку. Комнаты пронумерованы. Вам понадобится листочек и ручка. Заходите вы в первую комнату и на листочке пишите 1. Переходите через несколько входов в другие комнаты. Заходите в третью, пишите 3. Номер смотрите и пошли дальше по лабиринту. Идете, идете, идете, идете и тут ваш листочек заканчивается, вы записали последнее значение. Писать больше некуда. Вы мне по радиосвязи говорите, что у вас закончился листочек. Я говорю, чтобы вы шли дальше. Но это бессмысленно, если я сделаю еще пару шагов в соседние комнаты. Все, я потеряю выход, потому что я сейчас собрался идти в обратном порядке. Каким путем шел, таким и буду выходить, чтобы мне заблудится. Но если я сейчас пойду дальше, то я заблужусь, и не факт, что я выйду на какую-то из этих комнат. Хорошо, возвращайтесь. Идете вы обратно. Прошли там три комнаты и тут срывается с потолка сталактит, падает и пробивает вам листочек посередине. В листочке теперь дырка. Что вы делаете? Вы садитесь и начинаете плакать. Звоните и говорите, что у меня поврежден стек. У вас поврежден стек. Область адресов возврата разрушилась. Поэтому и в ОС повреждение стека или Stack Overflow Exception – переполнение стека, считается одной из самых серозных и опасных исключений. И специалисты считают, что после повреждения стека восстанавливать его нет смысла. То есть мне вас ждать нет смысла. Я конечно обращусь в соответствующие органы, в службу спасения, что вот вы где-то заблудились в лабиринте. Но, к сожалению, вероятность того, что программа после повреждения стека может быть восстановлена до работоспособного состояния очень и очень низкая. В программировании мы даже не пытаемся восстановить разрушенный стек, потому что это считается все, финиш. Это большая проблема. Так вот, как в жизни с лабиринтом, так и в программировании есть тоже такой листок бумаги, только это не листок бумаги, а страница памяти, потому что когда мы работаем в защищенном режиме, то у нас память разбивается не на сегменты, а на страницы памяти. У них есть разные размеры, несколько размеров, но стандартный размер страницы памяти, которая выделяется под работу со стеком равна 1 мегабайту. Обратите внимание, если мы здесь вызовем миллион раз сами себя и попытаемся построить миллион копий, у нас быстро закончится листочек и в итоге мы получим Stack Overflow Exception. Ну там гораздо меньше, мы сейчас не считаем размер адреса, мы это сейчас не считаем. Помимо адресов возврата стек может хранить еще кое что, но мы это не смотрим. Нас интересует стек, как область памяти для хранения адресов возврата из вызываемых нами процедур толи обычным способом вызываются разные процедуры. Толи рекурсивно. Замечательно. Мы еще раз закрепим, зайдем в Visio. Обратите внимание, вот идет наша программа. Идет оригинал программы, вот начальное значение counter, которое мы с вами рассматривали. Представьте, что у нас в качестве аргумента передается не 2, а 3. Как у нас в данном случае пойдет работа. Вот мы идем на условие, и если условие удовлетворяет истинности, то мы идем на его копию, видите, здесь строится его копия, здесь опять условие и до тех пор, пока условие не перестанет удовлетворять истинности у нас будут создаваться копии. Я почему показываю, это очень символично. Здесь у нас показана вот такая елочка. И мы видим что у нас постоянно идут переходы на копии, на копии и мы вот так выполняемся и возвращаемся и возвращаемся по стеку. Обратите внимание, вот эти числа 2, 1, 0. Представьте, что это вывод на экран. Давайте мы так немножко сдвинемся. И видим, что здесь мы выводим двойку. Counter = 2. Переходим дальше – выводим 1. Потом 0. И потом мы начинаем выполнять вторую половину метода. То есть, смотрите, мы выполняем только первую половину метода, ту что до самовызова. А здесь у нас идет то, что после самовызова. Когда мы уже возвращаемся у нас довыполняются части наших методов. Как копий, так и оригинала. Вот видите, по такому принципу у нас идет вызов и дальше, когда мы уже возвращаемся, здесь выводится 0. Здесь выводится 1, потому что эта копия хранила в себе 1. Здесь выводится 2. Уже с оригинала. А мы сейчас с вами возвращаемся в код и смотрим.
