# 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<string> words = new List<string> ();
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<int> fooBar = new Person<int>(10, "Foobar"); // или new(10, "Foobar")

int x = 5;
int y = 10;
Swap<int>(ref x, ref y) // или Swap(ref x, ref y)

class Person<T>
{
    public T Id { get; }
    public string Name { get; }

    public Person(T id, string name)
    {
        Id = id;
        Name = name;
    }
}

void Swap<T>(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<T, K>(K val);
```

## Анонимные методы

```C#
string foo = "Foo";
Func a = delegate(string a) {
    Console.WriteLine(a);
};
Func b = (a) => Console.WriteLine(a);
Func d = (string a) => Console.WriteLine(a); // Тип указывать не обязательно, если не использовать var
Func e = a => Console.WriteLine("FooBar"); // Если 1 параметр, то скобки не нужны
Func c = delegate { // Мы аргументы не используем, поэтому скобки можно не писать
    Console.WriteLine(foo); // Можно ссылаться на локальные переменные
};
var a = (string a, string b) => Console.WriteLine(a+b); // Неявная типизация

delegate void Func(string a);
```

Лямбды могут возвращать значения и их можно складывать и вычитать

```C#
Operation sum = (a, b) => a + b;
Operation product = (a, b) => a * b;
Operation op = sum;
op += product;
op -= sum;

delegate int Operation(int a, int b);
```

## *event*

```C#
Account a = new(50);
a.Notify += Console.WriteLine;

delegate void AccountHandler(string message);

class Account(int sum)
{

    public event AccountHandler? Notify;
    private int sum = sum;

    void Put(int amount) => sum += amount;

    void Take(int amount)
    {
        if (sum < amount)
        {
            Notify?.Invoke("Денег мала");
        }
        else
        {
            sum -= amount;
            Notify?.Invoke($"Снято {amount}");
        }
    }
}
```
