- •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();
Здравствуйте! Тема нашего сегодняшнего урока – Варианты использования методов. На этом занятии мы с вами научимся понимать более глубоко понимать работу методов. Более глубоко понимать работу рекурсии. Рекурсия – это такой специальный механизм, который используется при работе с методами мы научимся понимать перегрузку методов. И также будем использовать методы с опциональными именованными параметрами. Также мы рассмотрим еще несколько интересных примеров, которые позволят нам закрепить понимание методов и научится работать с методами. А мы вами перейдем к презентации, к первому слайду и посмотрим что же такое перегрузка методов. Обратите внимание, вот здесь представлены программные коды. Мы создаем метод с именем Method, который ничего не принимает и ничего не возвращает. В теле метода выводим на экран строку Hello. Ниже мы создаем, обратите внимание, мы создаем одноименный метод, который уже у нас есть. Но чем отличаются эти методы? Количеством параметров. Обратите внимание, у нас имеется два метода с одинаковым именем но с различным количеством или типом параметров. Ниже мы создаем еще один метод с именем Method, который принимает один целочисленный параметр. И получается у нас в нашей программе может иметься три, или больше одноименных метода, и эти методы должны отличатся количеством, либо типом параметров. Давайте перейдем к программному коду и посмотрим как это выглядит в коде. Мы заходим в первый пример.
1: using System;
2:
3: // Перегрузка методов.
4:
5: namespace Methods
6: {
7: class Program
8: {
9: // Перегруженные методы могут отличаться типом и количеством аргументов, а также ref и out параметрами.
10:
11: static void Function() // 1-я перегрузка.
12: {
13: Console.WriteLine("Hello!");
14: }
15:
16: static void Function(string s) // 2-я перегрузка.
17: {
18: Console.WriteLine(s);
19: }
20:
21: static void Function(int i) // 3-я перегрузка.
22: {
23: Console.WriteLine(i);
24: }
25:
26: static void Function(double d) // 4-я перегрузка.
27: {
28: Console.WriteLine(d);
29: }
30:
31: static void Function(string s, int i) // 5-я перегрузка.
32: {
33: Console.WriteLine(s + i);
34: }
35:
36: static void Function(int i, string s) // 6-я перегрузка.
37: {
38: Console.WriteLine(i + s);
39: }
40:
41: static void Function(ref int i, string s) // 7-я перегрузка.
42: {
43: Console.WriteLine(i + s);
44: }
45:
46: // Перегруженные методы не могут отличаться возвращаемыми значениями!
47: //static string Function(string s) // 8-я перегрузка.
48: //{
49: // return s;
50: //}
51:
52: // Перегруженные методы не могут отличаться друг от друга только параметрами ref и out!
53: //static void Function(out int i, string s)
54: //{
55: // i = 5;
56: // Console.WriteLine(i + s); // 9-я перегрузка.
57: //}
58:
59: static void Main()
60: {
61: Function(); // 1-я перегрузка.
62: Function("A"); // 2-я перегрузка.
63: Function(1); // 3-я перегрузка.
64: Function(3.14); // 4-я перегрузка.
65: Function("B ", 2); // 5-я перегрузка.
66: Function(3, " C"); // 6-я перегрузка.
67:
68: int variable = 5;
69: Function(ref variable, "D"); // 7-я перегрузка.
70:
71: // Delay.
72: Console.ReadKey();
73: }
74: }
75: }
На 11 строке мы создаем метод с именем Function, который ничего не принимает и ничего не возвращает. Выводит на экран строку. Ниже на 16 строке мы создаем одноименный метод Function, который принимает строковой аргумент и ничего не возвращает. Ниже на 21 строке мы строке мы создаем одноименный метод Function, который принимает один целочисленный аргумент и ничего не возвращает. Ниже на 26 строке мы создаем еще один одноименный метод с именем Function, который принимает Double аргумент и ничего не возвращает. Смотрите, ниже на 31 строке мы создаем еще один одноименный методы Function, который принимает два аргумента: первый строковой, второй – целочисленный. На 36 строке мы создаем еще один метод Function, который принимает два параметра, но обратите внимание, у нас уже имеется метод с такими параметрами. Но очередность этих параметров отличается. Если отличается очередность, то мы можем сказать, что по сути это разные методы. Смотрим ниже, у нас имеется на 41 строке метод с именем Function, который первый параметр принимает как ref параметр, обратите внимание, потому что на 36 строке у нас уже имеется метод с однотипными параметрами, которые идут в такой же очередности, как и на 41 строке. Но тем они отличаются. Тем что на 36 строке – это обычный параметр in – входной параметр. А на 41 строке – это у нас ref – выходной параметр. Помните мы говорили, что программист будь осторожен, если ты передашь методу в качестве первого параметра какую-то свою переменную, то возможно ее значение может изменится. Обратите внимание, еще раз мы видим что у нас имеется набор одноимённых методов. Но эти методы отличаются чем? Они отличаются типом и количеством аргументов. Типом и количеством параметров. И даже если у нас тип и количество параметров совпадает, и тогда мы что будем учитывать? Очередность этих параметров. Также можно учитывать входной ли это параметр. In это чисто входной параметр, а ref – выходной. Давайте посмотрим, еще несколько правил. Перегружаемые методы не могут отличатся возвращаемыми значениями. Почему? Потому что если вы представите себя компилятором то вы можете отличить перегрузки этих методов? Ну конечно, я их буду отличать по типу и количеству, по порядку аргументов. Но мне сложно их будет различить по типу возвращаемого значения. Я скажу сложно – но можно. Есть языки в которых перегрузки не отличаются друг от друга по типу возвращаемых значений по типу возвращаемых значений. Не отличаются. Таким языком является C#. Есть языки в которых перегрузки отличаются, и есть такой близки й к нам язык, который называется MSIL – Microsoft Intermediate Language. Его еще называют CIL – Command Intermediate Language. Это низкоуровневый язык, для работы с платформой .NET. Низкоуровневый язык, который содержит в себе удобочитаемые мнемоники для кодов виртуальной машины. Потому что виртуальная машина она принимает на вход коды, которые наш компилятор, наша программа преобразуется в эти коды, и потом эти коды принимает виртуальная машина и интерпретирует их. Но мы не будем об этом говорить. Если вам интересно вы можете немного почитать об этом языке, немного узнать. Мы на этом курсе его разбирать не будем, а уже наследующих курсах мы сделаем небольшой акцент и даже посмотрим несколько примеров с использованием этого промежуточного языка MSIL. Хорошо. Значит мы видим, что методы, которые встречаются у нас в программе, причем эти методы имеют одинаковое имя, то есть одинаковый идентификатор, то такие методы принято называть перегрузками. Вот мы и их называем: первая перегрузка метода, вторая перегрузка метода, третья и т.д. Правило следующее. В языке C# перегрузки методов не могут отличаться возвращаемыми значениями. Обратите внимание, Вы помните когда мы с вами говорили о сигнатуре метода? Что начиная с третьей спецификации Microsoft в понятие сигнатуры не включает возвращаемое значение. Сигнатура – это имя метода и его параметры. Возвращаемое значение оно само по себе. Почему так сделал Microsoft? Потому что общие спецификации, другие спецификации, не только языка C# тоже пришли к такому стандарту. Поэтому есть OMG консорциум. Object Managment Group. Который создает общие спецификации для языков программирования. Вы можете зайти на сайт omg.org и там вы увидите много спецификаций, спецификаций по ООП, по другим языкам и почитайте их, иногда это достаточно интересно. И поэтому так как все спецификации сказали, что давайте мы не будем в понятие сигнатуры включать возвращаемое значение, потому что когда я программист. Я говорю, чем отличаются друг от друга перегруженные методы? Сигнатурами. А если возвращаемое значение будет входить в сигнатуру, то это может ввести в такое небольшое заблуждение, мы будем думать перегружаемые методы отличаются возвращаемыми значениями. Но для того чтобы организовать в таких языках как MSIL перегрузку методов по возвращаемым значениям, пришлось создавать достаточно сложный механизм, который определял бы метод, определял конкретную перегрузку по типу возвращаемого значения. Это как можно определить? Я могу это определить только из контекста использования этого метода. Если я создаю переменную int а, а здесь string b, то только в таком случае нужно выходить за пределы понимания нашего методы, за пределы использования. И смотреть где же он используется. Этот как у людей бывает. Если я нахожусь в больнице, то кто я? Я пациент. Если я нахожусь в аудитории в качестве слушателя, кто я? Студент. Если я нахожусь, например, в автомобиле, то я водитель. Если я нахожусь, например, в поезде, то кто я? Пассажир. Мы пытаемся определить по контексту. Так вот в C# такой возможности нет. Потому что такая возможность вообще в .NET присутствует, но в C# ее закрыли для того чтобы не провоцировать возникновение сложных и ошибочных ситуаций. На самом деле платформа .NET имеет намного больше возможностей чем имеется в C#. Но они закрыты специально чтобы избежать появлению ошибок в программных кодах и сделать разработку проще. Разработка выглядит немножко ограниченной, программист чувствует себя в рамках, но тем не менее мы должны помнить, что мы – Microsoft программисты, мы чаще всего разрабатываем софт для бизнес решений. А бизнес не любит, когда с ним играют. Потому что, как сказал адвокат Терразини в фильме Спрут. «Когда начинают играть деньги вся другая музыка замолкает.» Поэтому бизнес не хочет чтобы мы игрались с его деньгами и вот экспериментировали. Поэтому нам дается четкий набор инструкций из которых мы строим свои программы. С одной стороны это ограничение кого-то смущает, а с другой стороны позволяет строить более правильно и более быстро программные решения. Хорошо. Смотрим дальше. 52 строка – перегруженные методы не могут отличатся друг от друга параметрами ref и out. Если мы попытаемся создать такой метод как этот. Вот идет попытка его создания. Давайте снимем комментарий мы получим ошибку. Cannot define overloaded method 'Function' because it differs from another method only on ref and out. То есть ref от out практически ничем не отличается. Поэтому система не расценивает две таких конструкции как перегрузки методов. Теперь давайте посмотрим на вызовы этих методов у нас имеется 7 полноценных перегрузок, мы посмотрели как эти перегрузки организуются. Теперь давайте попробуем вызвать эти методы. Обратите внимание, вот у нас 61 строка. Вызывается метод с 11 строки. 62 строка здесь у нас имеется один строковой аргумент и мы можем после Function поставить круглую скобку, у нас появляется вот такая подсказочка и мы можем здесь теперь листать имеющиеся перегрузки методов. Хорошо. И еще один важный момент. А теперь давайте подумаем а почему в ООП функции и процедуры начали называть методами. Не только C# почему вообще функции и процедуры принято называть методами. Смотрите, мы с вами уже говорили, что имя метода должно четко отражать именно вид деятельности этого метода. Представьте у меня на столе лежит карандаш. И вы мне говорите: «Александр, возьми карандаш!» Смотрите, я его беру правой рукой. Я его взял. Я положил его. Вы еще раз говорите: «Александр, возьми карандаш.» Я его беру левой рукой. Положил. Вы мне говорите еще раз: «Александр, возьми карандаш!» Я его беру одновременно двумя руками. Что я делаю? Что я выполняю? Я выполняю операцию по поднятию карандаша разными методами. Я выполнил некую функциональность. Но функциональность – это звучит слишком низкоуровнево. Здесь уже вы учитываете все мои мышцы, пальцы, как они работают, как они сгибались, как зажимали этот карандаш. Смотрите, я трижды поднял карандаш. Поднял карандаш разными методами. Понимаете почему такие конструкции языков как функции и процедуры собирательно принято называть методами. Метод выполнения определенной операции. Вы скажете: «Александр, напомни мне пожалуйста, операцию поднятия карандаша ты какими методами?» Первая перегрузка – правой рукой, вторая перегрузка – левой рукой, третья перегрузка двумя руками. Ну я мог там если снять ботинки, можно было бы пальцами ноги… Но я уже не буду так экспериментировать. Теперь мы с вами понимаем, что слово Метод – это как способ выполнения определенной операции. Сложно? Нет, это не сложно. Еще раз смотрим с вами на перегрузки. Давайте еще раз зайдем в презентацию, и посмотрим, что здесь подразумевается под перегрузкой. Под перегрузкой методов понимается создание нескольких методов (два или более) с одинаковым именем, но различным типом и/или количеством параметров. Наборы параметров перегруженных методов могут отличатся порядком следования, количеством, типом. Таким образом перегрузка необходима для того, чтобы избежать создания нескольких разноименных методов, выполняющих сходные действия. Помните я карандаш поднимал. Представьте, если бы у меня не было перегрузки, чтобы я сделал первый раз? Поднял карандаш. Второй – поднял карандаш. Третий – поднял карандаш. А так мне пришлось бы говорить что это поднимание карандаша левой рукой, поднимание карандаша правой рукой. Это звучит не очень хорошо. Я выполнил операцию по поднятию карандаша. Просто я выполнял операцию поднятия карандаша тремя разными методами. Хорошо.
А мы с вами переходим к следующей секции нашего урока и рассмотрим какие же у нас бывают разновидности аргументов. Или как мы их называем – параметров. Обратите внимание, у нас бывают позиционные параметры, именованные и опциональные. Позиционные – при вызове метода, аргументы передаются в соответствии с заданной позицией и их типом. Вот у нас имеется метод у него имеется два параметра. А вот здесь мы этот метод вызываем. Обратите внимание, в соответствии параметрам в списке параметров мы и передаете параметры. Потому что если я попытаюсь 5 и Hello поменять местами, то что получится? Ошибка. Именованные параметры. Мы видим, что например при вызове метода, аргументы передаются в соответствии с их именем и типом, при этом позиция не имеет значения. Но количество аргументов должно совпадать. Обратите внимание, если мы не хотим указывать параметры в нужной позиции, такое редко встречается, но возможность такая есть. У нас имеется такой же самый метод но я хочу чтобы сначала Hello, а потом 5 передать. Здесь мне предлагается использовать такой синтаксис. Укажи сначала имя аргумента, поставь две точки и напиши значение, которое ты хочешь передать этому параметру. И поэтому я здесь меняю местами. B уже идет первым параметром, я здесь явно указываю что я хочу сюда передать. А идет вторым параметром, хотя в настоящем методе при создании он был сделан первым. Понятно, если я хочу поменять параметры местами, я использую технику именованных параметров. И еще один вид параметров – это опциональные параметры. Обратите внимание, когда мы создаем метод, мы здесь можем что сделать? Мы можем взять и аргументам присвоить некие значения по умолчанию. При вызове метода, аргументам можно передавать значения в соответствии с их позицией, типом и именем, при этом, те аргументы, которым не передано какое-то значение, уже имеют значение по умолчанию. Что это значит? Что если я а здесь присвою единицу. То если этот параметр не передать, то а будет равна1 по по умолчанию. Теперь смотрим, здесь при вызове метода мы можем использовать совместно с опциональными параметрами и именованные, и обычный позиционный подход. Поэтому смотрите, если вот здесь мы вызываем метод и используем именованный параметр b. То а будет равна1. Смотрим дальше, вызываем метод. Обратите внимание, первый параметр у нас какой? 5. Значит выведем 5. И будет конкатенация с ОК. Потому что b по умолчанию равен ОК. А при третьем вызове что будет? А = 1, B = OK. Поэтому обратите внимание, подход с опциональными параметрами мы можем даже не указывать параметры, потому что у нас имеются некие значения по умолчанию, присвоенные этим параметрам. Три разновидности параметров: Позиционные, мы с ними уже работали – это строгий подход. Именованные – это когда мы хотим от строгого подхода и просто при вызове метода указывать имя параметра, которому мы хотим присвоить какое-то значение. И вот опциональные параметры, когда при создании метода, обратите внимание, при создании метода мы присваиваем аргументам некие значения по умолчанию позволяя пользователю в дальнейшем не указывать, просто не передавать эти аргументы при вызове метода. Просто? Просто. Давайте посмотрим, как выглядит в коде работа с такими разновидностями параметров.
1: using System;
2:
3: // Именованные аргументы методов
4:
5: namespace Methods
6: {
7: class Program
8: {
9: static int Difference(int height, int width)
10: {
11: return height - width;
12: }
13:
14: static void Main()
15: {
16: // Можно вызвать метод так:
17: int difference = Difference(6, 5);
18:
19: //difference = Difference(5, 6);
20:
21: Console.WriteLine("Разность равна: {0}", difference);
22:
23: // А теперь, вот так:
24: difference = Difference(width: 5, height: 6);
25:
26: Console.WriteLine("Разность равна: {0}", difference);
27:
28: Console.ReadKey();
29: }
30: }
31: }
Смотрим на именованные параметры методов. На 9 строке мы создаем метод с именем Difference, которые принимает два аргумента height и width. Возвращает целочисленное значение и в теле метода мы возвращаем разность аргументов. На 17 строке, обратите внимание, мы переменной difference присваиваем возвращаемое значение метода Difference. Мы передаем 6 и 5. Получается, что 6 попадает в height, а 5 в width. 6-5=1. Difference равна 1. Смотрим дальше, 21 строка, если мы поменяем местами, то у нас будет -1. Теперь смотрите на 24 строке мы вызываем метод Difference, но теперь явно указываем имя того параметра, которому мы хотим присвоить некоторое значение. И мы видим что в width мы передаем 5, хотя при создании метода он был поставлен вторым, но здесь мы его используем первым. Height, он был при создании поставлен первым, при вызове мы используем его вторым. Сказать, что это часто используемая техника – нельзя так сказать. Эта техника не часто используется, я б ы сказал что это уточняющая техника. Ну иногда параметры созданы в определенной последовательности, а когда мы читаем код, используем этот метод, наша логика подсказывает нам, что лучше можно было бы фразу построить вот так. Различие между тем-то и тем-то. Иногда хочется оттенить, чтобы другим программистам показать, что смотри ты задаешь в Difference именно width и height. Просто удобство, просто возможность. Нельзя сказать что это какая-то производственная необходимость, без которой нельзя прожить. Смотрим дальше, работа с перегрузками методов.
1: using System;
2:
3: // Перегрузка методов.
4:
5: namespace Methods
6: {
7: class Program
8: {
9: static void Operation() // 1-я перегрузка.
10: {
11: Operation("val", 10, 12.2);
12: }
13:
14: static void Operation(string value1) // 2-я перегрузка.
15: {
16: Operation(value1, 10, 12.2);
17: }
18:
19: static void Operation(string value1, int value2) // 3-я перегрузка.
20: {
21: Operation(value1, value2, 12.2);
22: }
23:
24: static void Operation(string value1, int value2, double value3) // 4-я перегрузка.
25: {
26: Console.WriteLine("{0},{1},{2}", value1, value2, value3);
27: }
28:
29: Static void Main()
30: {
31: Operation(); // 1-я перегрузка.
32: Operation("val"); // 2-я перегрузка.
33: Operation("val", 10); // 3-я перегрузка.
34: Operation("val", 10, 12.2); // 4-я перегрузка.
35:
36: // Delay.
37: Console.ReadKey();
38: }
39: }
40: }
Обратите внимание, на 9 строке здесь идет работа с перегрузками методов и одновременно с использованием именованных параметров. Смотрите, какие здесь могут возникнуть трудности. У нас имеются 4 перегрузки методов. Operation, operation с одним аргументом, operation с двумя аргументами и operation с двумя аргументами. В первом что у нас здесь происходит? На 11 строке в теле метода Operation я вызываю другую перегрузку метода Operation с 24 строки. И сюда я передаю три параметра. Видите, как система определила. Первое – это строковой параметр, второй – int, третий double. Не этот же метод подставился. Потому что если я бы был компилятором я бы понял, что мне нужно определить из имеющихся методов, они все одноименные, теперь мы начинаем определять по параметрам. Далее смотрим, в следующем методе, который принимает один аргумент у нас снова же происходит вызов метода с 24 строки, его четвертая перегрузка. И мы теперь видим, что в качестве первого аргумента, мы подставляем единственный аргумент этого метода. На 19 строке мы создаем еще одну перегрузку метода operation с двумя параметрами, и видим, что здесь снова подставляются два аргумента этого метода и вызывается перегрузка с 24 строки. Давайте смотрим дальше. Мы на 31 строке вызываем первую перегрузку метода. Он в свою очередь вызывает четвертую перегрузку на 32 строке мы вызываем вторую перегрузку, он в свою очередь вызывает, обратите внимание, четвертую. На 33 строке мы вызываем третью, а он в свою очередь вызывает четвертую. Какой запутанный код. У нас здесь по сути имеется перегрузка метода, к которой обращаются все. Мы видим что к одной перегрузке обращаются другие перегрузки. Не очень красиво, как-то запутанно, непонятно, надо смотреть на эти подсветки, учитывать количество аргументов, надо смотреть на подсветку синтаксиса. Вот кликаем по нему, на какой строке этот метод. Правда не очень удобно. Не удобно, посмотри что нам предлагает, как же нам предлагает Microsoft.
1: using System;
2:
3: // Методы с опциональными параметрами.
4:
5: namespace Methods
6: {
7: class Program
8: {
9: // Метод вызывается так же, если бы это были перегрузки.
10: static void Operation(string value1 = "val", int value2 = 10, double value3 = 12.2)
11: {
12: Console.WriteLine("{0}, {1}, {2}", value1, value2, value3);
13: }
14:
15: static void Main()
16: {
17: Operation(); // 1-я перегрузка.
18: Operation("val"); // 2-я перегрузка.
19: Operation("val", 10); // 3-я перегрузка.
20: Operation("val", 10, 12.2); // 4-я перегрузка.
21:
22:
23: // Нельзя поставить третий параметр value3 = 12.2, вместо второго параметра value2 = 10!
24: // 12.2 не может быть приведено к int — здесь C# пытается оставить по умолчанию третий параметр, а не второй.
25: //Operation("val", 12.2); // ОШИБКА!
26:
27: // В данном случае возможно использование именованных параметров.
28: // Они состоят из указания имени параметра, двоеточия и значения, которое мы передаем.
29:
30: Operation("val", value3: 12.2);
31: Operation(value2: 33, value3: 12.2);
32:
33:
