997 lines
22 KiB
Markdown
997 lines
22 KiB
Markdown
# 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);
|
||
```
|