# C\# ## Project file Project file - файл для конфигурации проекта в формате xml Поля: * OutputType (Exe | Dll) - во что компилировать * TargetFramework (net7.0) - версия .NET ## Переменные ```C# string name = "Tom"; string name2; name2 = "Bob"; ``` ## Константы ```C# const string NAME = "Tom"; ``` Константы: * Определяются только во время компиляции * Неявно являются статичными ## Типы данных * bool * sbyte (signed byte) * byte * short - 16 байт * ushort (unsigned) * int - 32 байта * uint * long - 64 байта * ulong * char - 16 байт. Знак unicode * float 32 байт * double - 64 байта * decimal - 128 бит * string * object - аля Python Object Числа в формате 3.14 по умолчанию double. Если нужен float или decimal, то используются суффиксы ```C# float a = 3.14f; decimal b = 3.14m; ``` Для целочисленных по умолчанию int ```C# int a = 5; uint b = 5u; long c = 5l; ulong d = 5ul; ``` ## Неявная типизация ```C# var hello = "Hello world"; ``` Для var объявление и инициализация должны идти вместе ```C# // Так низя: ошибка var c; c = 5; ``` ## Вывод в терминал ```C# Console.WriteLine("Привет"); Console.Write("Привет "); Console.Write("Мир"); ``` Выведет: ```text Привет Привет мир ``` ## Форматированный вывод ```C# string name = "John"; string age = 34; Console.WriteLine($"{name} is {age} years old") ``` ## Ввод данных ```C# string? name = Console.ReadLine(); int foo = Convert.ToInt32(Console.ReadLine()); ``` ## Простые операторы * \+ * \- * \* * / * % ## Преобразование базовых типов данных Операция сложения и вычитания возвращает int, если типы, на которые применяется операция, <= int (byte, short, int) ### Неявное преобразование Работает только на расширение ```C# byte a = 4; ushort = a; ``` ### Явное преобразовывание Работает на расширение и на сужение ```C# byte a = 5; byte b = 4; byte c = (byte) (a+b); ``` ## Ветвление ```C# int a = 5; if (a == 5) { Console.WriteLine("It works"); } else { Console.WriteLine("Cosmic ray!"); } ``` ### Тернарный оператор Краткая возвращающая форма if ```C# int x = 3; int y = 5; string z = x < y ? "Works" : "Cosmic ray"; ``` ### *Switch* ```C# int number = 1; switch (number) { case 1: Console.WriteLine("case 1"); goto case 5; case 3: Console.WriteLine("case 3"); break; case 5: Console.WriteLine("case 5"); break; default: Console.WriteLine("case whatever"); break; } ``` ### Циклы Всё как в С++ * *while* * *do-while* * *for* * *foreach* ## Массивы Массивы позволяют хранить заранее известное кол-во элементов вместе ### Инициализация массивов ```C# int[] a = new int[3] {10, 20, 30}; double[] b = new double[5]; float[] c = {1.5f, 2.3f}; // Поймёт, что нужна длина 2 ``` ### Длинна массивов ```C# int[] a = {10, 20}; int len = a.Length; ``` ### Методы ```C# int[] a = {10, 30, 20}; Array.IndexOf(a, 10) // Вернёт 0. -1, если не найдёт. Array.Sort(a); // Сортирует по возрастанию. Станет {10, 20, 30} ``` ### Двумерный массив ```C# int[,] a = new int[10,10]; ``` ### Динамический массив ```C# using System.Collections.Generic; List words = new List (); words.Add("Foo"); words.Add("Bar"); words.RemoveAt(1); ``` ## *Random* ```C# Random r = new Random(); int a = r.Next(); // От 0 до 2 миллиардов int b = r.Next(5); // От 0 до 5 (не включительно) int c = r.Next(3, 5); // От 3 до 5 ``` ## Функции ```C# void Foo() { Console.WriteLine("Hello"); } // Краткая форма void Bar() => Console.WriteLine("Hi"); ``` ### Параметры функции ```C# int Sum(int x, int y) => x + y; // Тут x и y - параметры Sum(5, 6); // Тут 5 и 6 - аргументы ``` #### Необязательные параметры ```C# void PrintPerson(string name, int age = 1) { Console.WriteLine($"{name} - {age}"); } PrintPerson("John", 5); PrintPerson(age: 5, name: "Bob"); // Именованные параметры ``` #### Передача по ссылке При передаче по ссылке и зменения внутри функции меняют оригинальное значение ```C# void Inc(ref int a) { a++; } void BadInc(int a) { a++; } int b = 5; Inc(ref b); // b == 6 BadInc(b); // b осталась 6 Inc(b); // Так низя Inc(5); // И так низя Inc(ref 5); // И так тоже низя ``` #### Выходной параметр Функция может иметь несколько выходных параметров, а так лучше return ```C# void Sum(int a, int b, out int result) { result = a + b; } int result; Sum(5, 6, out result); ``` #### Входные параментры Передача по неизменяемой ссылке. Судя по всему полезно только для массивов ```C# int Sum(in int a, in int b) { return a + b; } int a = 5; int b = 6; Sum(a, b); ``` #### *params* *params* позволяет передать не определённое кол-во параметров. После него не может быть больше параметров ```C# int Sum(params int[] nums) { int sum = 0; foreach (int i in nums) { sum += i; } return sum; } int[] nums = {1, 2, 3, 4}; Sum(nums); Sum(1, 2, 3); Sum(20); Sum(); ``` ### Возвращение значений ```C# int Sum1(int a, int b) { return a + b; } int Sum2(int a, int b) => a + b; ``` ### Рекурсия ```C# int Factorial(int n) { if (n == 0) return 1; return n * Factorial(n - 1); } ``` ### Функции внути функций ```C# int Sum2Arr(int[] arr1, int[] arr2) { int Sum(int[] arr) { int result = 0; foreach (int i in arr) { result += i; } return result; } return Sum(arr1) + Sum(arr2); } ``` ## *Enum* Enum используется для хранения состояния ```C# enum Operation { Add, Sub } Operation foo = Operation.Add; ``` ### Тип констант перечисления ```C# enum Time: byte { Morning, Afternoon, Evening, Night } ``` Тип обязательно должен быть целочисленным. По умолчанию int ### Задание значения для Enum ```C# enum DayTime { Morning = 3, Afternoon // 4 } ``` ## OOP OOP - зло, C# - OOP -> C# - зло ```C# namespace Foo { internal class Enemy { public int hp; public int armor; public int damage; public Enemy(int hp, int armor, int damage) { this.hp = hp; this.armor = armor; this.damage = damage; } public void Move() { Console.WriteLine("I am moving"); } public void Attack() { Console.WriteLine("I am attacking"); } } } ``` Конструкторов может быть несколько ### Инициализаторы ```C# Person tom = new Person {name = "Tom", company = { title = "Microsoft" }}; class Person { public string name = "Undefined"; public Company company; public Person() { company = new Company(); } } class Company { public string title = "Unknown"; } ``` ### Деконструкторы Деконструкторы - методы, чтобы "разобрать" объект на составные части ```C# class Person { public string name; public string age; public void Deconstruct(out string personName, out int PersonAge) { personName = name; personAge = age; } } ``` ### Структуры Структуру - аналог классов с особенностями: * Храняться на стеке * Нет наследования: нельзя использовать *abstract*, *virtual*, *protected* * При присваивании создаются копия * Имеется ключевое слово with ```C# Person tom; tom.name = "Tom"; tom.age = 1; Person bob = tom with { name = "Bob" }; Person Alice = new Person { name = "Alice", age = 5 }; Person Jan = new Person("Jan", 10); struct Person { public string name; public int age; public Person(string name, int age) { this.name = name; this.age = age; } } ``` ### Типы значений и ссылочные типы Типы значений (значимые типы) хранятся на стеке. При присваивании значимых типов создаётся копия. К таким типам относятся: * Целочисленные значения * Числа с плавающей точкой * *decimal* * *bool* * *char* * Перечисления * Структуры Ссылочные типы в куче, а ссылка на них в стеке. При присваивании ссылочных типов копируется только ссылка. К ним относятся: * *string* * *object* * Классы * Интерфейсы * Делегаты ## Пространства имён Пространства имён позволяют организовыывать код программы в логические блоки ```C# namespace Foo { namespace Bar { class Baz {} } } ``` Пространство имён подключается с помощью *using* ```C# using Foo; using Foo.Bar; // Для этого снача using Foo писать не надо global using Foo.Bar; // Подключает этот namespace везде. Зачастую такое в файле GlobalUsings.cs ``` ## Модификаторы доступа * *private* - только в рамках класса/структуры (по умолчанию для полей) * *private protected* - в рамках класса и наследников внутри сборки * *file* - только в текущем файле * *protected* - внутри класса и наследниках, которые могут находиться в других сборках * *internal* - доступен только внутри сборки (по умолчанию для классов) * *protected internal* - доступен из любого места в сборке и классах наследниках, которые могут быть в других сборках * *public* - достпупен везде ## Свойства ```C# class Person { string name = "Undefined"; public string Name { get { return name; } set { name = value; } } } ``` Можно и так ```C# class Person { public string Name { get; set; } public int Age { get; set; } = 69; public Person(string name) { this.Name = name; } } ``` ### Модификатор *required* Данный модификатор делает поле или свойство обязательным для инициализации ```C# Person tom = new Person(); // Ошибка Person bob = new Person { Name = "Bob" }; // Вот так хорошо class Person { public required string Name { get; set; } } ``` ## Перегрузка методов Перегрузка методов - возможность создать несколько методов, принимающих разное кол-во параметров или разные типы параметров, с одним и тем же именем. Выходной тип не может отличаться. ```C# int Sum(int a, int b) { return a + b; } int Sum(int a, int b, int c) { return a + b + c; } ``` ## *static* Статические поля, методы, свойства относятся ко всему классу. Обращение к ним идёт через сам класс. Статичные поля инициализируются во время запуска программы и находятся в специальной области памяти для static'ов. Статические классы могут содержать только статичные поля/свойста/методы. ```C# static class Person { public static int retirementAge = 65; public static int RetirementAge { get { return retirementAge; } set { retirementAge = value; } } public static int Foo(int bar) { return bar + 2; } } ``` ## Модификатор *readonly* Полям, которые были объявлены *readoly*, значение можно присваивать только при объявлении или конструкторе ```C# class Person { public readonly string name = "Undefined"; public Person(string name) { this.name = name; } public changeName(string name) { this.name = name; // Ошибка } } readonly struct Person2 { public readonly string Name { get; }; // Тут readonly не обязательно public int Age { get; }; // Тоже как-бы readonly } ``` ## Наследование ```C# Employee tom = new Employee("Tom", "Org"); tom.Name = "Tom"; tom.PrintName(); class Person { private string _name = ""; public string Name { get { return _name; } set { _name = value } } public Person(string name) { this.name = name; } public void PrintName() { Console.WriteLine(_name) } } sealed class Employee: Person { // sealed запрещает наследование от данного класса public string company; public Employee(string name, string company) : base(name) { this.company = company; base.PrintName(); // Обращение к функцианалу родителя } } ``` От статических классов тоже нельзя наследоваться. ## Преобразование типов Все классы наследуются от Object. Upcasting - неявное преобразование к типу, который находится вверху иерархии классов. Downcasting - явное преобразование типа к производному ```C# Employee tom = new Employee("Tom", "Org"); tom.Name = "Tom"; Person person = tom; Employee employee = (Employee)person; Employee? employee = person as Employee; // null если преобразовать не удалось if (person is Employee employee) { Console.WriteLine(employee.company); } else { Console.WriteLine("Преобразовать низя"); } class Person { private string _name = ""; public string Name { get { return _name; } set { _name = value } } public Person(string name) { this.name = name; } public void PrintName() { Console.WriteLine(_name) } } sealed class Employee: Person { public string company; public Employee(string name, string company) : base(name) { this.company = company; } } ``` ## Переопределение > Возможность изменить функцианальность метода родительского класса в дочернем. *virtual* помечает метод для переопределения в базовом классе, а *override* используется в дочернем ```C# class Foo { public virtual void func() { Console.WriteLine("Foo"); } public virtual int Baz { get => 5; set { Console.WriteLine(value); } } } class Bar : Foo { public override sealed void func() // sealed запрещает дальнейшее переопределение { Console.WriteLine("Bar"); } public override int Baz { get => 6; } } ``` ## Shadowing / Hiding ```C# class Foo { public const string class_ = "Foo"; public int Baz { get => 5; } public void Print() { Console.WriteLine("Foo"); } } class Bar : Foo { public new const string class_ = "Bar"; public new int Baz { get => 6; } public new void Print() { Console.WriteLine("Bar"); } } ``` ## Абстрактные классы > Для представления сущностей, не имеющих конкретного воплощения, предназначены абстрактные классы. От них нельзя создать объект. Абстрактный класс может содержать абстрактные методы и свойства, которые не могут быть private. Абстрактные методы не имеют тела. Все не абстрактные наследники должны определять все абстрактные методы и свойствая ```C# abstract class Transport { public abstract void Move(); public abstract string Name { get; set; } } class Car : Transport { public override void Move() { Console.WriteLine("Car moves"); } public override string Name { get; set; } = "Car"; } ``` ## Обобщения (generics) ```C# Person fooBar = new Person(10, "Foobar"); // или new(10, "Foobar") int x = 5; int y = 10; Swap(ref x, ref y) // или Swap(ref x, ref y) class Person { public T Id { get; } public string Name { get; } public Person(T id, string name) { Id = id; Name = name; } } void Swap(ref T x, ref T y) { T tmp = x; x = y; y = tmp; } ``` ## Оценка сложности > В программировании, вычислительную скорость алгоритмов обычно оценивают по количеству действий, который выполняет алгоритм, и по количеству используемой памяти Виды: * O(1) - constant time * O(n) - линейная * O(log n) - логарифмическая * O(n**2) - квадратичная ## Обработка ошибок ```C# try { // Тут злой код (он кидается) } catch { // Тут мы обрабатываем летящие в нас камни } finally { // This is fine... Anyway } ``` Иногда может использоваться bool ```C# int Square(string data) { if (int.TryParse(data, out var x)) { return x * x; } else { return default(int); } } ``` Можно обрабатывать конкретные ошибки ```C# int x = 1; int y = 0; try { int result = x / y; } catch (DivideByZeroException) when (y == 0 && x == 0) { Console.WriteLine("Оба ноль"); } catch (DivideByZeroException ex) { Console.WriteLine(ex.Message); } ``` ### Класс *Exception* > Класс *Exception* - базовый класс всех ошибок Поля: * InnerException * Message * Source - имя объекта/сборки, которое вызвало исключение * StackTrace * TargetSote - метод, в котором случилось исключение ### *Throw* ```C# try { throw new Exception("СТРАШНА ВЫРУБАЙ"); } catch { Console.WriteLine("This is fine"); } ``` ## Делегаты > Делегаты - указатели на методы. Методы должны полностью соответсвовать сигнатуре делегата. > Сигнатура - возвращаемый тип и передоваемые параметры Используются для: * Callbacks * Events - в языке есть хитрый механизм на это дело ```C# Message? mes = Hello; mes(); mes = new Hello().Display; mes(); mes = new Message(Hello); // Можно и так присвоить mes += new Hello().Display; mes(); // Вызывается и Hello, и Display mes += Hello; // Hello теперь будет вызываться 2 раза mes -= Hello; // Удалит последний Hello из списка вызова if (mes == null) { return; } mes += mes; // Делегаты можно складывать mes.Invoke(); // Тоже самое, что и просто вызвать делегат void Hello() { Console.WriteLine("Hello World"); } delegate void Message(); class Hello { public void Display() { Console.WriteLine("Hello there"); } } ``` Если в делегате несколько методов, то вернётся последнее значение. Делегаты могут быть параметрами методов и возвращаемым типом. ## Обобщённые делегаты ```C# delegate T Operation(K val); ``` ## Анонимные методы ```C# string foo = "Foo"; Func a = delegate(string a) { Console.WriteLine(a); }; Func b = (a) => Console.WriteLine(a); Func c = delegate { // Мы аргументы не используем, поэтому скобки можно не писать Console.WriteLine(foo); // Можно ссылаться на локальные переменные }; var a = (string a, string b) => Console.WriteLine(a+b); // Неявная типизация delegate void Func(string a); ```