# 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 если преобразовать не удалось 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") } } class Bar: Foo { public override void func() { Console.WriteLine("Bar") } } ```