-
Notifications
You must be signed in to change notification settings - Fork 0
/
Program.cs
2171 lines (1837 loc) · 177 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
using ConsoleApp1;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
namespace ConsoleApp1
{
/// <summary>
/// Добро пожаловать в "краткий" экскурс по C#;
/// Если ты ниче не понял, то это уже твои проблемы, а не мои;
/// Данный файл был создан во время изучения курсов по C# с канала "#SimpleCode" и использовался как конспект;
/// Если шарите в программировании, но не знаете C# - мне кажется, этого файла может хватит. Я не проверял;
/// Если нет, то рекомендую перейти по ссылке и посмотреть весь курс:
/// https://www.youtube.com/playlist?list=PLQOaTSbfxUtD6kMmAYc8Fooqya3pjLs1N
/// Данный код не предназначен для запуска, а лишь только для изучения синтаксиса!
/// </summary>
internal class Program
{
static void Main(string[] args)
{
cheeseburger();
// Вывод сообщения "Привет, мир!" в консоль;
Console.WriteLine("Hello, world!");
// По факту является обращением System.Console.WriteLine(string Value);
// Но т.к. мы используем "using System" то "System." можно убрать;
// --Типы переменных--
// Особые;
object ob = null; // "System.Object" | Базовый тип данных для всех типов данных, благодаря ему можно ссылаться на абсолютно разные типы данных;
dynamic dn = null;
// Целые числа;
sbyte sb_min = sbyte.MinValue; sbyte sb_max = sbyte.MaxValue; // "System.SByte" | -128 -> 127 | 1 byte;
byte b_min = byte.MinValue; byte b_max = byte.MaxValue; // "System.Byte" | 0 -> 255 | 1 byte;
short s_min = short.MinValue; short s_max = short.MaxValue; // "System.Int16" | -32768 -> 32767 | 2 byte;
ushort us_min = ushort.MinValue; ushort us_max = ushort.MaxValue; // "System.UInt16" | 0 -> 65535 | 2 byte;
int i_min = int.MinValue; int i_max = int.MaxValue; // "System.Int32" | -2147483648 -> 2147483647 | 4 byte;
uint ui_min = uint.MinValue; uint ui_max = uint.MaxValue; // "System.UInt32" | 0 -> 4294967295 | 4 byte;
long l_min = long.MinValue; long l_max = long.MaxValue; // "System.Int64" | -9223372036854775808 -> 9223372036854775807 | 8 byte;
ulong ul_min = ulong.MinValue; ulong ul_max = ulong.MaxValue; // "System.UInt64" | 0 -> 18446744073709551615 | 8 byte;
// Числа с плавающей точкой;
float fl_min = float.MinValue; float fl_max = float.MaxValue; // "System.Single" | -3.40282347E+38 -> 3.40282347E+38 | Точность 6-9 цифр | 4 byte;
double db_min = double.MinValue; double db_max = double.MaxValue; // "System.Double" | -1.7976931348623157E+308 -> 1.7976931348623157E+308 | Точность 15-17 цифр | 8 byte;
decimal dc_min = decimal.MinValue; decimal dc_max = decimal.MaxValue; // "System.Decimal" | оч_много -> оч_много | Точность 28-29 знаков | 16 byte;
// Символы;
char c = 'a'; // "System.Char" | Может содержать только один символ;
string s = "Hello, world!"; // "System.String" | Может содержать полноценный текст;
// Логика;
bool bl = true; // "System.Boolean" | true/false, правда/ложь;
// Ну и на закусочку, кстати;
var bebey = 10; // <- благодаря "var" компилятор сам постарается понять, какой тип назначить для переменной;
// Но подробнее об этом будет ниже;
// Пример того, как создается переменная:
int new_int; // Создаем переменную и задаем её тип;
new_int = 5; // Задаем переменной значение;
// Можно задавать тип переменных последовательно как на примере ниже:
int a, b; // <- Крутая фича;
a = 5;
b = 10;
// Ввод данных из консоли и дальнейший вывод их в ту же консоль;
Console.WriteLine("Введите ваше имя:");
string name;
name = Console.ReadLine(); // Собственно, ввод данных пользователем происходит именно здесь;
Console.WriteLine("Добро пожаловать, " + name + "!");
// Конвертация типа "string" в тип "int" и получение текущего года из даты;
Console.WriteLine("Введите ваш возраст:");
//int age = int.Parse(Console.ReadLine());
Console.WriteLine("Ваш год рождения: {0}", (DateTime.Now.Year - Convert.ToInt32("32"))); // Да, можно и таким образом форматировать вывод;
// Преобразование типа "string" в тип "double" в зависимости от локализации операционной системы;
// Если нихрена не понятно, просто вернешься к этому позже;
// Использование в дробных числах точки или запятой в некоторых регионах может отличаться;
// В данном случае мы создаем формат и говорим функции Convert.ToDouble что нам нужна именно точка, а не запятая;
string height = "1.90"; // Допустим, это рост в метрах с той самой точкой;
//"using System.Globalization" сверху необходим как раз для этой строчки;
NumberFormatInfo numberFormatInfo = new NumberFormatInfo()
{
NumberDecimalSeparator = ".",
};
double d_height = Convert.ToDouble(height, numberFormatInfo); // Конвертация будет завершена успешно, т.к. мы сказали конвертатору что нам нужна именно точка;
// Использование парсинга (преобразования) не через конвертер, а через сами типы данных;
string string_number = "9"; // Число в виде символа;
int parsed_number = int.Parse(string_number); // "int" таким образом преобразовывает значение переменной типа "string" в самого себя, т.е. "int";
double parsed_double = double.Parse("6,2"); // То же самое, только с "double";
double parsed_double_with_dot = double.Parse("5.4", numberFormatInfo); // Используем переменную "numberFormatInfo" чтобы сказать преобразователю, что нам нужна точка;
// Обработка недействительных значений при входе данных;
// С помощью "try" и "catch" программа не будет завершена из-за ошибки, а просто выведет строку в консоль при неуспешной конвертации;
string wtf = "25lol";
try
{
int parsed_int = int.Parse(wtf);
Console.WriteLine("Успешная конвертация!");
}
catch (Exception)
{
Console.WriteLine("Ошибка при конвертации!");
}
// Функция "TryParse" не может выдать ошибку при выполнении, т.к. при недействительном значении введенных данных просто не будет преобразовывать переменную;
string wtf2 = "39rofl";
int try_parse_number; // Если значение выше недействительно, здесь при преобразовании просто останется число 0 (см. ниже);
int.TryParse(wtf2, out try_parse_number); // "out" означает, что при корректном выполнении функции мы преобразовывем переменную (см. выше);
bool result = int.TryParse("39", out try_parse_number); // Эта функция также может выдавать true/false при успешной/неуспешной конвертации;
if (result)
{
Console.WriteLine("Успешная конвертация!");
}
else
{
Console.WriteLine("Ошибка при конвертации!");
}
// --Бинарные операторы (работают с двумя операндами) и особенности работы с ними--
int i1 = 2;
int i2 = 5;
int result4 = i1 / i2; // = 0 | т.к. нужен тип "double";
double result5 = i1 / i2; // = 0 | т.к. оба числа типа "int";
double i3 = 4;
int i4 = 10;
double result6 = i3 / i4; // = 0.4 | т.к. одно из чисел имеет тип "double";
double result7 = i4 / i3; // = 2.5;
double result8 = (double)i1 / i2; // = 0.4 | т.к. полученный результат сразу преобразовывается в "double";
int result9 = 10 % 3; // = 1 | Деление с остатком;
// --Унарные операторы (работают лишь с одним операндом) инкремента и декремента во всей своей красе--
int n1 = 0;
n1++; // = 1 | Увеличивает число на 1;
Console.WriteLine(n1++); // = 1 | т.к. сначала идет вывод значение переменной "n" в консоль, а только потом число увеличится на 1;
int m = 0;
Console.WriteLine(++m); // = 1 | т.к. увеличение числа произойдет перед выводом в консоль;
n1--; // Декремент, уменьшает число на 1;
Console.WriteLine(n1);
int n2 = 1;
n2 = ++n2 * n2; // = 4 | т.к. (1 + 1) * (1 + 1) | Префиксный инкремент имеет наибольший приоритет при просчете;
// Именно поэтому было сначала увеличено значение переменной "n2", а потом мы получили значение суммы уже увеличенных переменных;
int n3 = 1;
n3 = n3++ * n3; // = 2 | т.к. (1 * 1) + 1 | Постфиксный инкремент имеет наименьший приоритет при просчете;
// --Операторы "if" и "else", ничего особенного--
if (n3 > 1) // Если выполняется условие, т.е. мы получаем "true", то исполняется код ниже;
{
n3--;
}
else if (n3 < 2) // Если условие выше не было выполнено, но выполнено это условие, то исполняется код ниже;
{
n3 = 0;
}
else // Если же не было выполнено ни одно из условий, то исполняется код ниже;
{
n3++;
}
// --Оператор "switch"--
int l = 4; // Задаем переменную;
switch (l) // Скармливаем переменную оператору;
{
case 0: // "case <значение>" нужен для того, чтобы сравнить переменную "l" со своим значением;
Console.WriteLine("Это ноль без палочки. А нет, все же с палочкой.");
break; // Необходим для "закрытия" кейса;
case 1:
{
Console.WriteLine("Это единица."); // Кейс можно закрыть скобками на случаей, если так будет удобнее;
}
break;
case 4:
Console.WriteLine("ЧОТЫРИ!!!!!!");
break;
case 5: // В данном случае мы оставляем кейс без "break" и логики, т.к. они находится под следующим кейсом;
case 6:
Console.WriteLine("Это или пять, или шесть, кто его знает? Число Шрёдингера какое-то...");
break;
default: // Выполняется если значение переменной не было найдено среди значений "кейсов". Аналог "else";
Console.WriteLine("Ты втираешь мне какую-то дичь!");
break;
}
string k = "Alexey"; // Также работает с типом "string";
switch (k)
{
case "Alexey":
Console.WriteLine("Поздравляем, вы Alexey!");
break;
case "Gregory": // Если один из кейсов имеет тип "string", то и все остальные кейсы должны иметь тот же тип для корректной работы;
Console.WriteLine("Соболезную, вы Gregory...");
break;
}
// --Цикл "while"--
// Будет выполняться до тех пор, пока получает в себя значение "true";
int count1 = 0;
while (count1 < 5) // Сначала идет проверка условия, потом выполняется цикл;
{
Console.WriteLine(count1);
count1++;
} // В итоге мы получим числа 0, 1, 2, 3, 4;
int count2 = 0;
do // В данном случае сначала выполняется цикл, а уже потом идет проверка условия;
{
Console.WriteLine(count2);
count2++;
} while (count2 < 5); // В итоге мы получаем числа 0, 1, 2, 3, ;
// --Цикл "for"--
// Работает до тех пор, пока получает в себя значение "true", как и "while";
// Однако имеет некоторые свои особенности;
int length = 10;
for (int i = 0; i < length; i++) // В данном случае переменная "i" не может выйти за рамки цикла;
{
Console.WriteLine(i);
}
int o = 0;
for (; o > -10; o--) // Можно также пропустить введение внутренней переменной, однако наличие ";" обязательно для компилятора;
{
Console.WriteLine(o);
}
int u = 10;
for (; u < 30;) // Можно пропустить преобразование переменной, однако наличие ";" обязательно;
{
Console.WriteLine(u);
u++;
System.Threading.Thread.Sleep(300); // Ожидаем 300 мс перед тем, как снова выполнять цикл;
}
for (; ; ) // Бесконечный цикл;
{
break; // Я пошутил, прервать его можно оператором "break";
}
for (int i = 0; ; i++) // Можно использовать самые разные вариации по мере необходимости;
{
if (i > 3)
{
break; // Данный оператор прерывает ВЕСЬ цикл;
}
}
for (int i = 0, j = 5; i < 10 && j < 12; i++, j++) // Можно объявлять и использовать сразу несколько переменных;
{
Console.WriteLine(i);
Console.WriteLine(j);
}
for (int i = 0; i < 10; i++)
{
if (i == 5)
{
continue; // Переходим на следующую итерацию цикла, пропуская число 5. Не прерывает весь цикл, только текущую итерацию;
}
Console.WriteLine(i);
}
// --Вложенные циклы--
// Цикл внутри цикла, логично.
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Цикл №1, итерация: {0}", i); // Сначала мы показываем переменную "i" 1 раз;
for (int j = 0; j < 10; j++)
{
Console.WriteLine("\tЦикл №2, итерация: {0}", j); // Потом показываем переменную "j" 10 раз и снова показываем "i" 1 раз, а потом "j" 10 раз... И так пока "i" не станет 10;
}
}
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 20; j++)
{
Console.Write("#"); // Просто добавляем один символ в консоль 20 раз;
}
Console.WriteLine(); // Переход на следующую линию, как только "#" будет отрисована 20 раз;
} // Всего должно получиться 10 линий по 20 "#" в каждой;
// --Тернарный оператор (работает с тремя операндами)--
// [1й операнд - условие] ? [2й операнд - if_true] : [3й операнд - if_false];
// Пример с избыточным кодом (как делать нежелательно):
bool accessAllowed;
string storedPassword = "qwerty";
string enteredPassword = Console.ReadLine();
if (enteredPassword == storedPassword)
{
accessAllowed = true;
}
else
{
accessAllowed = false;
}
Console.WriteLine(accessAllowed);
// Пример с использованием тернарного оператора (как делать круто и модно);
accessAllowed = enteredPassword == storedPassword ? true : false;
Console.WriteLine(accessAllowed);
// Еще один пример использования тернарного оператора;
int inputData = int.Parse(Console.ReadLine()); // Запрашиваем число из консоли и парсим его в переменную типа "int";
int outputData = inputData < 0 ? 0 : inputData; // Если число меньше 0, то выводим 0, если больше, то пишем введенное число без изменений;
Console.WriteLine(outputData);
// ---Массивы---
// --Одномерные массивы--
// тип_элементов_массива [] имя_массива;
int[] int_array; // = null (отсутствует ссылка) | Создаем новый массив, который будет содержать элементы (переменные) типа "int";
int_array = new int[10]; // Отводим место для массива в оперативной памяти, определяя размер массива, т.е. он будет содержать максимум 10 элементов;
// Индексы массива будут выглядеть как [0], [1], [2], [3] и т.д.;
int_array[0] = 5; // Вводим индекс в [] и потом задаем число;
int_array[3] = 10; // В дальнейшем по индексам можно обращаться в массив для получения заданных значений;
//int_array[16] = 4; -> выдаст ошибку, т.к. индекс выходит за рамки размеров массива;
Console.WriteLine(int_array[3]); // Выводим число 10, которое задали ранее, обращаясь к массиву по индексу;
int int_from_int_array = int_array[0]; // Создаем переменную и задаем ей значение 5 из массива;
// --Инициализация массива--
int[] new_array1 = new int[5] { 3, 4, 1, 7, 6 }; // Сразу инициализируем массив и вводим в него данные;
// int[] new_array1 = new int[5] { 3, 4, 1, 7}; -> выдаст ошибку, т.к. не хватает пятого числа;
int[] new_array2 = new int[] { 3, 4, 1, 7 }; // Можно не указывать размер для массива, и тогда он задаст его сам исходя из вводных данных;
// int[] new_array2 = new int[]; -> выдаст ошибку, т.к. без вводных данных компилятор не может инициализировать массив, ибо не знает его размеров;
int[] new_array3 = new[] { 5, 1, 3, 8 }; // Можно не указывать тип массива при инициализации, он возьмет его из вводных данных;
// int[] new_array3 = new [5]; -> выдаст ошибку, т.к. не был определен тип и компилятор не понимает, под какой тип ему выделять память;
int[] new_array4 = { 5, 1, 3, 8 }; // Можно не вводить "new[]", если тип и количество элементов можно определить из вводных данных;
int[] new_array5 = Enumerable.Repeat(5, 10).ToArray(); // Добавляем число "5" в массив "new_array5" 10 раз. "ToArray()" необходим, чтобы преобразовать это все в массив;
int[] new_array6 = Enumerable.Range(3, 5).ToArray(); // Начинает отсчет от числа "2", создает 5 элементов, т.е. вывод в итоге будет {3, 4, 5, 6, 7};
// --Вывод массива--
int[] my_array = { 4, 1, 5, 7, 6 };
for (int i = 0; i < my_array.Length; i++) // Используем цикл "for", ограничивая число его итераций размером массива, который получаем при помощи "my_array.Length";
{
Console.WriteLine(i);
} // В итоге в косноль будут выведены числа 4, 1, 5, 7, 6;
int min_value_in_my_array = my_array.Min(); // Выводит минимальное число из массива, т.е. "1";
int max_value_in_my_array = my_array.Max(); // Выводит максимальное число из массива, т.е. "7";
// При отсутствии "using System.Linq" функции "Min()" и "Max()" не будут работать!
int sum_values_in_my_array = my_array.Sum(); // Выводит сумму всех чисел в массиве;
Console.WriteLine(my_array.Where(i => i % 2 == 0).Sum()); // Выводит сумму только четных чисел, используя функцию "Where" и lamba-функцию;
// "i % 2 == 0" здесь является условием выбора элементов массива, а потом из выбранных элементов мы получаем сумму;
Console.WriteLine(my_array.Where(i => i % 2 != 0).Min()); // Выводит наименьшее нечетное число из массива;
int[] new_array7 = { 11, 16, 20, 11, 18, 4, 20 };
int[] distincted_new_array7 = new_array7.Distinct().ToArray(); // Убираем повторяющиеся значения из массива;
int[] ordered_new_array7 = new_array7.OrderBy(i => i).ToArray(); // Сортируем числа в массиве в порядке возрастания;
int[] desс_ordered_new_array7 = new_array7.OrderByDescending(i => i).ToArray(); // Сортируем числа в массиве в порядке убывания;
Array.Sort(my_array); // Сортируем "my_array" в порядке возрастания с помощью статической функции класса "Array", т.е. самого массива;
int number_bubumber = Array.Find(my_array, i => i < 5); // Находит число ниже пяти и присваевает значение переменной;
// Функция ищет число начиная с индекса [0] и до конца списка, возвращая первое же число, подпадающее под условия;
int number_bubumber2 = Array.FindLast(my_array, i => i < 5); // Находит число ниже пяти, начиная с конца списка и идя до начала списка;
int[] numbers_bubumbers = Array.FindAll(my_array, i => i < 5); // Находит все числа ниже пяти, и возвращает массив с ними;
int number_bubumber3 = Array.FindIndex(my_array, i => i == 5); // Возвращает индекс находящегося в массиве числа, чтобы потом можно было получить его через "my_array[индекс]";
int number_bubumber4 = Array.FindIndex(my_array, i => i == 3); // = -1 | т.к. индекс для данного числа не был найден. Вообще этого числа в массиве нет, прикинь?
Array.Reverse(my_array); // "Переворачивает" массив, теперь начало это конец, а конец это начало (ауф);
int[] my_array2 = my_array.Where(i => i > 5).ToArray(); // Получаем нужные числа, упаковываем в массив и задаем его в переменной;
int my_array_first = my_array.Where(i => i > 5).First(); // Получаем первое попавшееся число больше 5;
int[] my_array3 = new int[3];
int my_array3_first = my_array3.Where(i => i > 5).FirstOrDefault(); // Если таких чисел нет, либо массив пустой, то функция возвращает стандартное значение для типа;
// В нашем случае для типа "int" это число 0;
// --Индексы и диапазоны--
int[] new_array8 = { 4, 11, 87, 36, 90, 1 };
Console.WriteLine(new_array8[new_array8.Last()]); // Получаем последний элемент из массива, т.е. цифру "1";
/* Инфа для C# 8.0+
В данном проекте ничего тут работать не будет, ибо .NET Framework. Именно поэтому это все закомменчено, ага;
Обрати внимание, что для примера мы берем массив "new_array8" который был создан вот прям чуточку выше;
Поднимаешь глаза и обалдеешь, когда его найдешь, вот настолько он близко;
Console.WriteLine(new_array8[^1]); -> получаем последний элемент из массива, т.е. цифру "1";
Стоит отметить, что индексация с конца списка начинается с [^1] (единицы), а с начала списка начинается с [0] (нуля);
Именно поэтому используя символ "^" мы пишем "1" чтобы получить последний элемент;
Console.WriteLine(new_array8[^2]); -> получаем предпоследний элемент из массива, т.е. цифру "90";
Console.WriteLine(new_array8[0..4]); -> получаем значения с индексов [0], [1], [2], [3];
Индексация тут уже привычная, но с "изюминкой";
Чтобы получить значения с [0] до [3] мы должны написать "0..4", т.е. 4й индекс является "гранью"/"концом" и не попадает в список;
Console.WriteLine(new_array8[..4]); -> получаем значения с индексов [0], [1], [2], [3] не указывая начало;
Т.е. мы получаем значения с самого начала списка, и до установленного значения;
Console.WriteLine(new_array8[3..]); -> получаем значения с индексов [3], [4], [5] не указывая конец;
Т.е. мы получаем значения с установленного значения, и до конца списка;
Console.WriteLine(new_array8[^2..]); -> получаем значения с индексов [4], [5] т.к. за начало мы берем второй индекс с конца;
Console.WriteLine(new_array8[^4..^2]); -> получаем значения с индексов [2], [3], [4] т.к. за начало мы берем четвертый индекс с конца, а за конец второй индекс с конца;
Console.WriteLine(new_array8[..^3]); -> получаем значения с индексов [0], [1], [2], [3] т.к. за конец мы берем третий индекс с конца;
string str = "Hello, world"; -> тип "string" по факту тоже является массивом, ООП во всей своей красе, брух;
Console.WriteLine(str[0]); -> таким образом мы можем получить первую букву "H" из нашей фразы "Hello, world";
Console.WriteLine(str[^1]); -> а вот уже таким образом мы можем получить последнюю букву "d" из нашей фразы "Hello, world";
Всякие диапазоны по типу [0..4] тут работают точно так же, как и с обычными массивами (потому что "string" это тоже массив, лол);
*/
// --Многомерные массивы--
// --Двумерный прямоугольный массив--
// тип_элементов_массива [,] имя_массива;
int[,] two_dim_array; // -> наличие запятой "," между "[" и "]" обязательно, если необходимо создать именно двумерный массив;
two_dim_array = new int[3, 5]; // -> создаем двумерный массив с условно треями строками и пятью колонками;
two_dim_array[0, 3] = 1; // -> задаем значение "1" по координатам массива [0, 3];
Console.WriteLine(two_dim_array[0, 3]); // считываем значение "1" по координатам массива [0, 3];
int[,] two_dim_array2 = new int[3, 5]; // -> альтернативный метод создания двумерного массива, занимающий меньше кода;
int[,] two_dim_array3 = new int[3, 5]
{
{ 1, 2, 3, 4, 5 }, // 3 строки, по 5 элементов в каждой;
{ 6, 7, 8, 9, 10 }, // Если в первой строке уже находится 5 элементов, то в следующей также должно быть 5 элементов, т.к. это прямоугольный массив;
{ 11, 12, 13, 14, 15 }
}; // Да, именно таким образом можно создавать двумерные массивы с заранее заданными значениями;
int[,] two_dim_array4 = new int[,] // Можно не задавать размеры массива заранее, компилятор считает размер из вводных данных;
{
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 }
};
int[,] two_dim_array5 = // Да, и так тоже можно;
{
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 }
};
foreach (var item in two_dim_array5) // "foreach" создает переменную "item" для каждого элемента из массива "two_dim_array5";
{
Console.WriteLine(item); // Используем элемент как нам нужно либо же просто выводим в консоль;
}
int two_dim_array_length = two_dim_array.Length; // Узнаем, сколько элементов в массиве | 3 * 5 = 15;
int two_dim_array_rank = two_dim_array.Rank; // Узнаем, сколько измерений в нашем массиве | 2;
for (int i = 0; i < two_dim_array_rank; i++)
{
Console.WriteLine(two_dim_array.GetLength(i)); // Узнаем длину каждого измерения | 3 и 5;
}
int[,] two_dim_array6 = new int[4, 6];
Random random = new Random(); // Создаем переменную класса "Random", т.к. без этого нельзя обратиться к нему для получения случайного числа;
for (int i = 0; i < two_dim_array6.GetLength(0); i++) // Получаем длину первого измерения;
{
for (int j = 0; j < two_dim_array6.GetLength(1); j++) // Получаем длину второго измерения;
{
two_dim_array6[i, j] = random.Next(100); // Генерируем для каждого элемента случайное значение с ограничением от 0 до 100;
}
}
// --Ступенчатые (зубчатые) массивы--
int[][] massives_inside_massives_wow = new int[5][]; // -> объявление двумерного зубчатого массива;
// По факту работает как одномерные массивы внутри одномерного массива;
// За счет этого у каждой "строки" основного массива могут быть самые разные размеры, в отличие от обычного двумерного массива [,];
massives_inside_massives_wow[0] = new int[4]; // -> создаем новый массив размером в 4 внутри основного массива. Да, так просто;
massives_inside_massives_wow[0][3] = 10; // -> задаем значение "10" индексу "3" внутри массива с индексом "0" который находится внутри основного массива. Ух, так и голову сломать можно;
int[] massive_from_massive_with_massives_inside = massives_inside_massives_wow[0]; // -> получаем массив с индексом "0" из массива с массивами;
for (int i = 0; i < massives_inside_massives_wow.Length; i++) // -> получаем длину основого массива, в нашем случае это "5";
{
for (int j = 0; j < massives_inside_massives_wow[i].Length; j++) // Получив индекс, обращаемся к каждому массиву внутри основного массива;
{
Console.WriteLine(j); // Ну и наконец выводим число из массива внутри массива;
}
}
// Трехмерные массивы работают по тем же принципам. Можно как создавать массивы внутри массивов, так и создать трехмерный массив через [,,];
int sum = a_plus_b(5, 4); // Обращаемся к методу "a_plus_b", который складывает два числа "5" и "4", и получаем в итоге "9";
}
// ---Функции и методы---
// модификаторы тип_возвращаемого_значения название_метода(параметры)
// {
// тело метода
// }
static int a_plus_b(int a, int b) // -> т.к. мы складываем две переменных типа "int", то в тип_возвращаемого_значения мы ставим "int";
{
return a + b; // Данный метод должен возвращать какие-то данные, именно для этого здесь нам нужен "return";
// Т.е. мы возвращаем данные туда, откуда эта функция была вызвана (см. строчку 498);
}
static void print_line() // "void" позволяет нашему методу не возвращать каких-либо значений, т.е. не использовать "return";
// Мы также не запрашиваем никаких значений, т.к. для выполнения метода они не нужны;
{
Console.WriteLine("Активирован протокол самоуничтожения. Ваш компьютер взорвется через 3, 2, 1..."); // Мы просто выводим текст в консоль;
}
// --Перегрузка методов--
/// <summary>
/// Суммирует то, что можно суммировать.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
// Небольшая фича. Если смотреть код через VisualStudio и навести на метод ниже, то можно увидеть описание, которые мы написали выше;
static int please_sum(int a, int b) // Обычный метод с двумя переменными на входе;
{ // Вызывается через "please_sum(число_1, число_2)";
return a + b;
}
static int please_sum(int a, int b, int c) // Уже по факту является перегрузкой метода "please_sum";
{ // Т.е. мы можем вызвать ту же функцию, но на входе будет три переменных;
return a + b + c; // Вызывается через "please_sum(число_1, число_2, число_3)";
}
static double please_sum(double a, double b) // Также перегрузка метода "please_sum";
{ // Здесь мы уже можем ввести две переменных уже не типа "int", а типа "double";
return a + b;
}
static void cool_method()
{
int a = 0; // Переменные внутри "static" метода нельзя использовать из других методов;
}
int program_int = 0; // "Не-static" переменные внутри непосредственно "класса", в нашем случае класса "Program" (см. строчку 11, самое начало) также нельзя использовать внутри "static" методов;
static int static_program_int = 5; // Данную переменную уже можно использовать внутри "static" методов;
static void use_non_static_int_from_class()
{
static_program_int++;
Program program = new Program(); // Однако же для использования "не-static" переменной мы может создать новый экземпляр нашего класса "Program";
program.program_int++; // И уже в этом экземпляре получать, менять значение и делать что вздумается. Это ООП, детка;
}
// --Области видимости--
static void area_of_visibility1()
{
int var = 20;
}
static void area_of_visibility2()
{
//var = 30; -> мы не можем использовать переменную из другого метода. Нужно создавать свою переменную;
}
static int static_int = 4;
static void area_of_visibility3()
{
int static_int = 10;
Console.WriteLine(static_int); // В данном случае выведется число "10", т.к. мы задали внутри метода переменную "static_int";
// Она же, в свою очередь, перекрывает переменную из класса "Program", внутри которого находится метод;
// При этом, если не задавать переменную "static_int" и сделать её запрос, то будет взято число из класса "Program", т.е. "4";
}
// --Ссылочные (reference) и значимые (value) типы--
// --Стек (stack) и управляемая куча (heap)--
// Здесь будет очень много букафф, но тема важная. Если ниче не понятно, просто вернись к ней позже;
static void method()
{
int a = 7; // Если навести на тип переменной, т.е. на "int", то можно увидеть, что этот тип является "структурой", или же "struct";
// Структуры размещается в "стеке", или же в "stack" - некоем объеме в оперативной памяти с высокой скоростью но небольшим размером для хранения значений в методах и пр.;
System.Int32 b = 4; // Частичка ООП в глаз попала походу, не так ли? Да, "int" является попросту кратким псевдонимом класса "System.Int32" для более удобного использования;
int c = a; // Не стоит пропускать эту строчку из вида, она нам понадобится. Просто запомни, что есть переменная "c", в которую мы скопировали данные из переменной "a";
Random random = new Random(); // Тип "Random" уже является "классом", или же "class", и размещается уже в "управляемой куче", или же "heap";
int[] array1 = new int[4]; // При наведении на "int" нам покажет "struct" и это может ввести тебя в заблуждение, ведь массивы на самом деле "классы", а не "структуры".;
Array array2 = new int[4]; // Альтернатива созданию массива. Если навести на "Array", то можно увидеть, что это действительно "class";
int key = (int)ConsoleKey.F12; // "ConsoleKey" является "enum" и также хранится в стеке, или же "stack";
// Стек (stack) имеет маленький размер (1 мб), работает крайне шустро, и очищается сразу после выполнения метода;
// Управляемая куча (heap) может иметь размер до 8 тб, все зависит от компа и архитектуры, и очищается только "Сборщиком мусора" (Garbage Collector);
// Структуры, или же "struct" и энумерации, или же "enum", по факту унаследованы от "System.ValueType";
} // <- как только метод будет завершен, стек моментально очищается, т.к. те же переменные "a", "b", "c" и "key" больше нет смысла хранить;
// Переменная "a" является отдельным объектом в памяти, как и "b", и "c". Почему "c"? Потому что мы попросту скопировали данные из переменной "a";
// В этом и есть, собственно, смысл значимых типов "struct" и "enum". Метод закончился и выполнил свою работу, расходимся по домам, здесь не на что смотреть;
// Хотя погоди-ка, а что с переменными "array1" и "array2"? Хороший вопрос, ведь эти переменные как раз таки ссылочного типа; Что это значит?
static void check_my_back()
{
int[] array = new int[7]; // -> создавая массив, мы выделяем место в оперативной памяти, а конкретнее - управляемой куче;
// Говоря проще, переменная "array" является лишь ссылкой на то место в оперативной памяти, в которой находится сам массив;
// Именно поэтому мы используем "new" -> чтобы именно "создать" массив и выделить под него место в оперативной памяти;
// А через "int[] array" мы просто создаем ссылку, которую можем потом использовать, и говорим, объекты какого типа ссылка может хранить;
// Именно поэтому данный тип и называется "ссылочным". И сам объект, на который ведет ссылка, может храниться в памяти достаточно долго;
// Однако кое-что в этом примере хранится и на "стеке", а конкретнее - ссылка, т.е. переменная "array", и будет храниться там до тех пор, пока не будет выполнен метод;
int[] second_array = array; // -> в данном случае мы попросту создали вторую ссылку на уже созданный массив, т.е. память в куче выделяться не будет, ибо объект уже существует;
}
static void pubg_or_playstation5()
{
int a = 5; // -> мы условно создаем "объект" с именем "a", значение которого является "5";
return_incremented(a); // -> здесь при помощи функции мы пытаемся увеличить значение переменной "a" на +1 (см. ниже);
Console.WriteLine(a); // = 5 | Почему так? Смотри ниже;
int[] array = new int[3];
set_value_in_array(array);
Console.WriteLine(array[0]); // = 1 | Почему так? Да ниже ты уже посмотри, господи...
}
static void return_incremented(int a) // Не смотря на то, что имя у этой переменной то же, что и выше, это уже совершенно иной "объект";
{
a++; // Как только переменная покинет контекст данного метода, ее значение будет удалено из памяти;
} // И по возвращению обратно, никакого увеличения числа не будет. Мы просто создали новый объект, увеличили его значение на +1, и на этом больше ничего;
static void set_value_in_array(int[] array) // Здесь мы передаем ссылку на существующий в управляемой куче объект;
{
array[0] = 1; // Мы задаем индексу [0] значение "1";
} // Имея правильную (или моднее сказать валидную?) ссылку на нужный объект мы можем получить данные из любого места, в т.ч. вернувшись обратно;
// --null--
// Че это ваще такое;
static void help_me()
{
bool b; // = false | Значение "false" является стандартным при объявлении переменной типа "bool";
int a; // = 0 | Так же, как и значение "0" является стандартным для переменных типов "int", "double" и т.д.;
double d; // = 0 | Догадываешься почему? Да. Именно потому, что это структуры, т.е. типы значимого типа - они не могут быть "null", они должны хранить какое-то значение;
Random random; // = null | А вот это уже класс. И если у него нет какого-либо значения по умолчанию, то нам выдаст "null";
int[] array; // = null | Почему? Потому что отсутствует ссылка на существующий объект. Это как создать ярлык на рабочем столе, не указав путь к программе;
Program program; // = null | И это ярлык, к слову, таки занимает место в стеке. Нам же нужно потом как-то обращаться к этому ярлыку, верно?
string str; // = null | Интересно, не так ли? Да, тип "string" является классом и может иметь значение "null";
int[] int_array = new int[3]; // Создадим для еще одного примера массив и выделим ему место в памяти;
int_array = null; // <- здесь мы, грубо говоря, удаляем из "ярлыка" ссылку/путь к реальному массиву в управляемой куче;
//Console.WriteLine(int_array[0]); <- если бы мы сейчас попытались вытащить данные из массива, то получили бы ошибку, т.к. у ярлыка больше нет пути к массиву;
}
// --Оператор объединения с null--
// ??;
static void best_method()
{
string str = "Hello, world!";
Console.WriteLine(str);
str = null;
string result;
if (str == null)
{
result = "Ошибка - нет данных!";
}
else
{
result = str;
} // Данный код не эффективен, ведь это все можно уместить в одну строчку;
Console.WriteLine(str ?? "Ошибка - нет данных!"); // Используя оператор "??" мы сокращаем пример выше до минимальных размеров;
result = str ?? ""; // Или же:
result = str ?? string.Empty; // Тот же самый эффект, что и с "";
Console.WriteLine("Количество символов = {0}", result.Length);
// --Оператор присваивания объединения с null--
// ??=;
// Внимание! Работает только на C# 8.0+
// str ??= string.Empty; -> в таком случае для примера выше нам можно не создавать переменную "result". Мы просто превращаем значение с "null" в значение string.Empty, или же "" (пустота, ничего);
// Console.WriteLine("Количество символов = " + str.Length) | = 0, т.к. "" (пустота, тьма, ничего, космос);
// int[] array = null;
// array ??= new int[0]; -> так же работает и с массивами, чтобы не напороться на ошибку. Если ссылки на действительный массив нет, то мы создаем новый и обращаемся следующей строке к нему;
// Console.WriteLine("Количество элементов в массиве = " + array.Length);
// --Оператор условного null--
// ?.;
int[] array = null;
Console.WriteLine("Сумма элементов массива = {0}", array?.Sum()); // Для того, чтобы у нас вдруг не появилось исключения, мы исползуем оператор ?.;
// Т.е. прежде чем мы обратимся к массиву, на который ведет переменная "array", работающая как ссылка, чтобы выполнить функцию "Sum()";
// С этим оператором мы проверяем, действительна ли данная "ссылка", и если же нет, то функция "Sum()" выполняться не будет, таким образом мы избегаем исключения;
// Однако из-за того, что ничего не выполняется, нам выведет просто "Сумма элементов массива = ";
Console.WriteLine("Сумма элементов массива = {0}", array?.Sum() ?? 0); // -> таким образом мы выводим "Сумма элементов массива = 0" если ссылка на массив недействительна;
}
// --Ключевое слово "ref"--
// Передача аргументов по ссылке;
static void main_method()
{
int a = 2;
not_ref_method(a); // a = 2;
// Как мы уже знаем, мы не можем так просто изменять структуры из другого метода, т.к. мы просто создаем новую структуру и копируем в нее значение;
// Не смотря на то, что в методе мы присвоили переменной "a" значение "-3", не смотря на одинаковое имя это две разных переменных;
// Именно поэтому по возвращению обратно мы не увидим изменений;
ref_method(ref a); // a = -5;
// Однако благодаря ключевому слову "ref" мы можем создать ссылку на место нашей переменной в оперативной памяти;
// И тогда все изменения внутри вызванного метода отразятся и по возвращению обратно;
// В таком случае нам необходимо иметь слово "ref" как на вызове метода...
}
static void not_ref_method(int a)
{
a = -3;
}
static void ref_method(ref int a) // Так и на приеме;
{
a = -5;
} // Конечно, в таком виде данная фича бесполезна, но этот пример просто более нагляднее, чтобы показать суть ключевого слова "ref";
struct my_struct
{
public int a;
public float b;
public double c;
}
static void juicy_method()
{
my_struct new_struct = new my_struct();
set_my_new_struct_pls(ref new_struct); // Более правильный пример использования подобной фичи;
}
static void set_my_new_struct_pls(ref my_struct _struct)
{
_struct.a = 4;
_struct.b = 7.0f; // -> благодаря "f" мы даем понять компилятору, что это именно тип "float";
_struct.c = 3.2d; // -> ну а здесь то же самое, только тип "double";
}
static void another_cool_method() // Еще один интересный момент с ключевым словом "ref", который будет тебе явно интересен;
{ // И касается он уже не структур, а классов;
int[] array = { 1, 6, 3, 2 }; // Массивы ведь являются классами, верно? Что будет, если мы используем ключевое слово "ref" с ним?
why_are_we_still_here(array); // array = {1, 6, 3, 2} | Здесь мы скопировали ссылку в метод, а в самом методе задали переменной значение "null";
// Однако по возвращении наша ссылка все еще осталась нетронутой, она все еще ведет туда, куда и вела;
just_to_suffer(ref array); // array = null | Здесь мы как бы создали "ссылку" на "ссылку", грубо говоря;
// Именно поэтому, задав переменной значение "null", по возвращении здесь наша переменная "array" уже тоже является "null";
i_can_feel_my_leg(ref array); // array = int[10] | С этой штукой все должно встать на свои места для тебя. Смотри;
// Мы создали новую "ссылку" на нашу "ссылку" под именем "array", которая вела куда-то в управляемую кучу, где хранился массив;
// И в методе мы задали нашей "ссылке" путь к новому массиву размером в 10, который там же в методе и создали;
// --Ссылочные локальные переменные--;
int b = array[0]; // Создаем переменную "b" и присваеваем ей значение из индекса "0" массива "array";
b = -5; // <- мы просто создали новую переменную и изменили ее значение, на массив это никак не повлияло;
ref int c = ref array[0]; // Однако создав ссылочную локальную переменную и привязав её к индексу массива...
c = -5; // Задавая значение ей мы уже будет работать напрямую с массивом, т.е. здесь мы присвоили индексу "0" значение "-5";
ref int d = ref and_my_arm(array); // Также если наш метод возвращает ссылочную переменную...
d = 10; // То мы также сможем взаимодействовать с самим массивом;
}
static void why_are_we_still_here(int[] array) // Мы получаем ссылку на массив, а не сам массив;
{
array = null; // Скопировав ссылку, данный метод удаляет свою ссылку, не трогая ту, с которой она была скопирована;
}
static void just_to_suffer(ref int[] array) // Обрати внимание на ключевое слово "ref" - оно указывает, что нам должна прийти именно "ссылка" на "ссылку"
{
array = null; // И, соответственно, имея "ссылку" на "ссылку", мы задаём той "ссылке" значение "null";
}
static void i_can_feel_my_leg(ref int[] array)
{
array = new int[10]; // А здесь мы создаем новый массив и копируем путь к нему нашей ссылке;
}
static ref int and_my_arm(int[] arr) // Данный метод возвращает не просто переменную, но ссылочную переменную из-за ключевого слова "ref" перед "int";
{
return ref arr[1]; // Вернув ссылочную переменную, тот метод, из которого был вызван данный метод, сможет использовать лишь одну переменную для взаимодействия с конкретным индексом массива;
}
// --Ключевое слово "out"--
// --Разница между "ref" и "out"--
// По факту, "out" делает практически все то же, что и "ref", разница лишь в деталях;
static void even_my_fingers()
{
int a;
// the_body_ive_lost(ref a); <- мы не можем использовать в методе, где на входе должен быть "ref", переменную у которой нет значения;
the_comrades_ive_lost(out a); // <- а вот в методе, где на входе должен быть "out" как раз можем;
the_body_ive_lost(ref a); // <- и после использования метода выше, т.к. переменной "a" из-за "out" обязательно должно быть выдано значение, мы можем использовать её с "ref";
}
static void the_comrades_ive_lost(out int value) // В методе с "out" на входе мы обязаны задать значение переменной;
{
value = 5; // Без этого компилятор будет сильно ругаться и выдаст ошибку;
}
static void the_body_ive_lost(ref int value) // При этом, в методе с "ref" на входе мы не обязаны делать с переменной что-либо, у нас просто есть такая возможность и все;
{ // Но нам обязательно нужно иметь правильную ссылку на переменную с каким-либо значением, а не "null";
}
static void press_f_to_pay_respect()
{
string str = Console.ReadLine();
int.TryParse(str, out int result); // -> интересный момент. Если добавить сюда "out", то в случае неудачной попытки парсинга благодаря этому ключевому слову...
Console.WriteLine(result); // Нам будет выведено число "0", т.е. стандартное значение для переменной типа "int", т.к. "out" ОБЯЗАН присвоить переменной значение;
}
// --Ключевое слово "in"--
static void sus_method()
{
int a = 7; // Создаем переменную;
another_sus_method(a); // Отправляем её в метод, добавлять ключевое слово "in" при этом не нужно;
}
static void another_sus_method(in int value) // Обращаем внимание на ключевое слово "in" на вводе;
{
Console.WriteLine(value); // <- Мы можем считывать переменную "value";
// value = 3; <- Но не можем никак её изменить, иначе компилятор выдаст нам ошибку;
// В чем смысл использования ключевого слова "in"?
// (Примечание - создавать ссылки имеет смысл только для структур и энумераций. Переменные, в которых "хранятся" классы и так уже являются ссылками);
// В том, что мы с его помощью создаем ссылку на то место в оперативной памяти, в которой находится значение реальной переменной;
// Именно поэтому благодаря "ref" и "out" мы могли менять значение переменной "извне", т.к. имели на нее прямую ссылку;
// Обычно при вводе переменной в метод значение попросту копируется и создаётся новая переменная, и для нее выделяется новое место в оперативной памяти;
// А так, если значение переменной занимает очень много места в памяти (например, тебе необходимо изменить четыре переменных типа "double" по 8 байт каждая)...
// Мы можем создать ссылку на это место, сэкономив место в памяти и при этом гарантируя, что реальное значение изменено не будет;
// Особенно это касается тех случаев, когда мы создаем свои структуры, и в них находится очень много данных;
// Тогда при использовании ссылочных переменных мы не только выигрываем в памяти, но и в производительности;
}
// --Ключевое слово "params"--
static void rofl_method()
{
int a = pls_sum(3, 5); // <- вызываем обычный метод сложения двух чисел;
int b = pls_sum(a, 10, 7); // <- вызываем перегруженный метод сложения уже трех чисел;
// Согласись, что создавать отдельную перегрузку для метода будет довольно накладно, если мы вдруг захотим сложить не три числа, а пять, десять или двадцать;
int c = sum_pls(10, 15, a, 38, 40, 19, b, 34, 20, 49); // <- вызываем уже другой метод, в котором на входе стоит ключевое слово "params";
here_we_go_again("Hello, world!", 10, '%', 2.4f, false); // <- отправляем в еще один метод значения самых разных типов, таких как "int", "string", "char" и т.д.;
// Чтобы понять что это вообще такое, посмотри в код этого метода;
}
static int pls_sum(int a, int b) // Обычный метод;
{
return a + b;
}
static int pls_sum(int a, int b, int c) // Перегрузка предыдущего метода;
{
return a + b + c;
}
static int sum_pls(params int[] parameters) // Благодаря "params" мы можем иметь на входе столько чисел, сколько нам потребуется без лишних перегрузок;
{ // Обрати внимание, что на входе мы также имеем массив "int[] parameters";
int result = 0; // По факту, из-за "params" нам не придется перед вызовом этого метода создавать новый массив через "new int[]" и запихивать туда переменные;
for (int i = 0; i < parameters.Length; i++) // Мы просто пишем числа или пишем переменные через запятую -> (1, 2, 3, 4, 5);
{
result += parameters[i]; // Ну и здесь собственно складываем поочередно с каждым значением из массива;
}
return result;
}
static void here_we_go_again(params object[] parameters) // Мы определяем тип данных в массиве как "object", т.е. общий, "родительский" тип для вообще всех типов;
{ // Именно благодаря этому при вызове данного метода мы можем вводить значения вообще любых типов от "int" до "string", и массив их будет спокойно хранить;
string message = "Тип данных {0], значение {1}";
foreach (var item in parameters) // Здесь "var" говорит компилятору, что мы не знаем какие конкретные типы здесь будут, и он должен определять их сам;
{
Console.WriteLine(message, item.GetType(), item); // Ну и, собственно, вывод в консоль сообщения, типа и значения каждой переменной в массиве;
}
}
// --Необязательные параметры методов--
static void i_have_no_ideas_how_to_name_this_method()
{
int a = sum_again(3, 5); // <- вызываем метод, складываем чиселки и получаем результат;
int b = sum_again(4, 10, true); // <- вызываем метод, складываем чиселки, выводим результат в консоль и получаем обратно;
}
static int sum_again(int a, int b, bool enableLogging = false) // Можно задать значения параметров метода, если их наличие при вызове метода необязательно;
{ // Т.е. можно как написать "sum_again(3, 5)", и тогда вывода в консоль не будет, так и "sum_again(4, 10, true)", если мы хотим вывести результат в консоль;
int result = a + b;
if (enableLogging) Console.WriteLine("{0} + {1} = {2}", a, b, result); // <- к слову, можно писать "if" и без фигурных скобок, но тогда нужен ";" в конце;
return result;
}
// --Именованные параметры методов--
static void ok_boomer()
{
int a = sum_again(b: 3, a: 5); // <- да, мы можем передавать значения методу и в обратном порядке, главное использовать имена;
int b = and_sum_again(value2: 9, value1: 4); // <- еще один наглядный пример c использованием метода ниже, где переменные называются "value1" и "value2";
}
static int and_sum_again(int value1, int value2)
{
return value1 + value2;
}
// --Рекурсия--
// --Методы и стек--
// --Переполнение стека (stack overflow)--
static void hello_choomba()
{
Console.WriteLine("Hello, choomba!");
hello_choomba(); // <- вызов метода внутри себя же и называется рекурсией. По факту, некий аналог бесконечного цикла, только в виде метода;
// Т.е. это практически то же самое, что и использование "while(true)";
// Однако в случае с использованием "while(true)" нам абсолютно по барабану на стек, его размер и переполнение;
// Рекурсия же ограничена максимальным размером стека, т.к. каждый вызов метода заполняет его память;
// Рано или поздно, если рекурсия бесконечная, то память кончится и нам выдаст ошибку "StackOverflow";
// Отследить "стек вызовов" это можно запустив отладку в VS, и посмотрев вниз;
// Почему кончается память? Потому что стек хранит данные, из какого метода был вызван данный метод, чтобы вернуться и продолжить выполнение;
// А т.к. мы вызываем себя же внутри себя, и стек хранит инфу о том, куда нужно вернуться, "call stack" начинает заполняться огромным количеством вызовов;
// Рекурсия ОБЯЗАНА иметь условия для выхода, если ты не хочешь, чтобы программа схлопнулась;
}
static void bye_choomba(int i = 0)
{
Console.WriteLine(i);
if (i >= 3) return; // <- более удачный пример использования рекурсии, т.к. мы имеем условие для выхода;
i++; // Но лучше так, конечно, не делать. Есть более удачные примеры использования рекурсии, но на это нужно слишком много кода;
bye_choomba(i);
}
// --Преобразование и приведение примитивных типов--
static void xbox_five()
{
byte o = 5;
int a = 4;
double b = 3.29;
float c = a; // <- нам необязательно конвертировать "int" во "float" вручную. Это называется "неявным" (или же "implicit") преобразованием, т.е. автоматическое;
playstation_ten(a); // <- мы даже можем запихнуть "int" в метод, где на входе должен быть "float";
a = o;
c = (float)b; // <- здесь же мы не можем так просто преобразовать "double" во "float"
// Именно поэтому нам необходимо использовать "неявное" (или же "explicit") преобразование, т.е. ручное;
// Так что указываем перед переменной в скобках (float) тип, в который нам нужно преобразовать значение типа "double";
// Почему так происходит и в чем дело?
// Нам просто-напросто не дают по своей глупости (а мы, программисты, иногда очень сильно тупим) внезапно потерять кучу данных из-за ошибки в коде;
int var1 = 10;
float var2 = var1; // <- "float" может хранить бОльший диапазон значений, чем "int", так что мы используем расширяющее преобразование;
double var3 = var2; // <- а "double", в свою очередь, может хранить бОльший диапазон, чем "float";
// И если мы пойдем в обратном порядке...
var2 = (float)var3; // <- то нам нужно дать компилятору понять, что нам действительно нужно "обрезать", или же сузить диапазон до допустимых для типа "float" значений;
var1 = (int)var2; // <- и так же, как и в предыдущем случае мы вручную "обрезаем" диапазон с "float" до "int";
playstation_ten((float)var3); // <- ну и, т.к. на входе метода нам нужен "float", а у нас "double", то мы используем явное преобразование;
int v = 3;
bool boolean = Convert.ToBoolean(v); // <- в булевую так просто не преобразовать, поэтому мы используем "Convert.ToBoolean()" в данном случае;
double bb = 2.4; // <- мы можем просто ввести "2.4", если заданный тип это "double";
float f = 2.4f; // <- однако в случае с типом "float" мы уже не сможем так сделать, т.к. компилятор думает что мы вводим "double";
// Поэтому после значения "2.4" нам необходимо поставить "f", т.е. "float", чтобы уточнить, что мы вводим именно "float" значение;
int k = v + (int)f; // <- если мы хотим получить значение типа "int", то в данном случае нам нужно преобразовать "float" в "int";
string s = "5";
int j = int.Parse(s); // <- в случае, если нам необходимо преобразовать "int" в "string", то мы не сможем просто использовать "(int)";
int u = Convert.ToInt32(s); // <- еще один вариант преобразования "string" в "int";
}