3.27M
Category: programmingprogramming

C#

1.

Язык программирования
С#

2.

Введение в C#
• На сегодняшний момент язык программирования C# один из самых
мощных, быстро развивающихся и востребованных языков в ИТотрасли. В настоящий момент на нем пишутся самые различные
приложения: от небольших десктопных программок до крупных
веб-порталов и веб-сервисов, обслуживающих ежедневно
миллионы пользователей.
• C# является языком с Си-подобным синтаксисом и близок в этом
отношении к C++ и Java. Поэтому, если вы знакомы с одним из
этих языков, то овладеть C# будет легче.
• C# является объектно-ориентированным и в этом плане много
перенял у Java и С++. Например, C# поддерживает полиморфизм,
наследование, перегрузку операторов, статическую типизацию.
Объектно-ориентированный подход позволяет решить задачи по
построению крупных, но в тоже время гибких, масштабируемых и
расширяемых приложений. И C# продолжает активно развиваться,
и с каждой новой версией появляется все больше интересных
функциональностей.

3.

СКАЧАТЬ
VISUAL STUDIO

4.

Начало работы. Visual Studio

5.

6.

7.

8.

9.

10.

11.

Сборка проекта
Исходный код на языке C#
Структура
проекта

12.

Структура программы

13.

Инструкции
Базовым строительным блоком программы являются инструкции
(statement). Инструкция представляет некоторое действие,
например, арифметическую операцию, вызов метода, объявление
переменной и присвоение ей значения. В конце каждой
инструкции в C# ставится точка с запятой (;). Данный знак
указывает компилятору на конец инструкции. Например, в проекте
консольного приложения, который создается по умолчанию, есть
такая строка:
Console.WriteLine("Hello, World!");

14.

Регистрозависимость
C# является регистрозависимым языком. Это значит, в
зависимости от регистра символов какое-то
определенные названия может представлять разные
классы, методы, переменные и т.д. Например, для
вывода на консоль используется метод WriteLine его имя начинается именно с большой буквы:
"WriteLine". Если мы вместо "Console.WriteLine"
напишем "Console.writeline", то программа не
скомпилируется, так как данный метод обязательно
должен называться "WriteLine", а не "writeline" или
"WRITELINE" или как-то иначе.

15.

Комментарии
Есть два типа комментариев: однострочный и многострочный.
Однострочный комментарий размещается на одной строке после
двойного слеша //. А многострочный комментарий заключается между
символами /* текст комментария */. Он может размещаться на
нескольких строках. Например:
/*
первая программа на C#,
которая выводит приветствие на консоль
*/
Console.WriteLine("Привет");
// Выводим строку "Привет"
Console.WriteLine("Добро пожаловать в C#"); // Выводим строку "Добро
пожаловать в C#"

16.

Типы данных
В языке C# есть следующие базовые типы данных:
bool: хранит значение true или false (логические литералы). Представлен системным типом System.Boolean
bool alive = true;
bool isDead = false;
byte: хранит целое число от 0 до 255 и занимает 1 байт. Представлен системным типом System.Byte
byte bit1 = 1;
byte bit2 = 102;
sbyte: хранит целое число от -128 до 127 и занимает 1 байт. Представлен системным типом System.SByte
sbyte bit1 = -101;
sbyte bit2 = 102;
short: хранит целое число от -32768 до 32767 и занимает 2 байта. Представлен системным типом System.Int16
short n1 = 1;
short n2 = 102;
ushort: хранит целое число от 0 до 65535 и занимает 2 байта. Представлен системным типом System.UInt16
ushort n1 = 1;
ushort n2 = 102;

17.

int: хранит целое число от -2147483648 до 2147483647 и занимает 4 байта. Представлен системным типом
System.Int32. Все целочисленные литералы по умолчанию представляют значения типа int:
int a = 10;
int b = 0b101; // бинарная форма b =5
int c = 0xFF; // шестнадцатеричная форма c = 255
uint: хранит целое число от 0 до 4294967295 и занимает 4 байта. Представлен системным типом
System.UInt32
uint a = 10;
uint b = 0b101;
uint c = 0xFF;
long: хранит целое число от –9 223 372 036 854 775 808 до 9 223 372 036 854 775 807 и занимает 8 байт.
Представлен системным типом System.Int64
long a = -10;
long b = 0b101;
long c = 0xFF;
ulong: хранит целое число от 0 до 18 446 744 073 709 551 615 и занимает 8 байт. Представлен системным
типом System.UInt64
ulong a = 10;
ulong b = 0b101;
ulong c = 0xFF;

18.

float: хранит число с плавающей точкой от -3.4*1038 до 3.4*1038 и занимает 4 байта.
Представлен системным типом System.Single
double: хранит число с плавающей точкой от ±5.0*10-324 до ±1.7*10308 и занимает 8 байта.
Представлен системным типом System.Double
decimal: хранит десятичное дробное число. Если употребляется без десятичной запятой, имеет
значение от ±1.0*10-28 до ±7.9228*1028, может хранить 28 знаков после запятой и занимает 16
байт. Представлен системным типом System.Decimal
char: хранит одиночный символ в кодировке Unicode и занимает 2 байта. Представлен
системным типом System.Char. Этому типу соответствуют символьные литералы:
char a = 'A';
char b = '\x5A';
char c = '\u0420';
string: хранит набор символов Unicode. Представлен системным типом System.String. Этому типу
соответствуют строковые литералы.
string hello = "Hello";
string word = "world";
object: может хранить значение любого типа данных и занимает 4 байта на 32-разрядной
платформе и 8 байт на 64-разрядной платформе. Представлен системным типом System.Object,
который является базовым для всех других типов и классов .NET.
object a = 22;
object b = 3.14;
object c = "hello code";

19.

Неявная типизация
Ранее мы явным образом указывали тип переменных, например,
int x;. И компилятор при запуске уже знал, что x хранит
целочисленное значение.
Однако мы можем использовать и модель неявной типизации:
var hello = "Hell to World";
var c = 20;

20.

Консольный ввод-вывод
Консольный вывод
Для вывода информации на консоль мы уже использовали встроенный
метод Console.WriteLine. То есть, если мы хотим вывести некоторую
информацию на консоль, то нам надо передать ее в метод
Console.WriteLine:
string hello = "Привет мир";
Console.WriteLine(hello);
Console.WriteLine("Добро пожаловать в C#!");
Console.WriteLine("Пока мир...");
Console.WriteLine(24.5);

21.

Нередко возникает необходимость вывести на консоль в одной
строке значения сразу нескольких переменных. В этом случае мы
можем использовать прием, который называется интерполяцией:
string name = "Tom";
int age = 34;
double height = 1.7;
Console.WriteLine($"Имя: {name} Возраст: {age} Рост: {height}м");

22.

Есть другой способ вывода на консоль сразу нескольких значений:
string name = "Tom";
int age = 34;
double height = 1.7;
Console.WriteLine("Имя: {0} Возраст: {2} Рост: {1}м", name, height,
age);

23.

Console.Write
Кроме Console.WriteLine() можно также использовать метод
Console.Write(), он работает точно так же за тем исключением, что
не добавляет переход на следующую строку, то есть последующий
консольный вывод будет выводиться на той же строке.
string name = "Tom";
int age = 34;
double height = 1.7;
Console.Write($"Имя: {name} Возраст: {age} Рост: {height}м");

24.

Консольный ввод
Кроме вывода информации на консоль мы можем получать
информацию с консоли. Для этого предназначен метод
Console.ReadLine(). Он позволяет получить введенную строку.
Console.Write("Введите свое имя: ");
string? name = Console.ReadLine();
Console.WriteLine($"Привет {name}");
В данном случае все, что вводит пользователь, с помощью метода
Console.ReadLine() передается в переменную name.

25.

Convert.ToInt32() (преобразует к типу int)
Convert.ToDouble() (преобразует к типу double)
Convert.ToDecimal() (преобразует к типу decimal)

26.

Арифметические операции языка
C#
Бинарные арифметические операции:
+ Операция сложения двух чисел:
int z = 10 + 12; // 22
- Операция вычитания двух чисел:
int z = 10 - 6; // 4
* Операция умножения двух чисел:
int z = 10 * 5; // 50
/ операция деления двух чисел:
При делении стоит учитывать, что если оба операнда представляют целые числа, то результат также будет округляться до
целого числа:
double z = 10 / 4; //результат равен 2
Несмотря на то, что результат операции в итоге помещается в переменную типа double, которая позволяет сохранить дробную
часть, но в самой операции участвуют два литерала, которые по умолчанию рассматриваются как объекты int, то есть целые
числа, и результат то же будет целочисленный.
Для выхода из этой ситуации необходимо определять литералы или переменные, участвующие в операции, именно как типы
double или float:
double z = 10.0 / 4.0; //результат равен 2.5
% Операция получение остатка от целочисленного деления двух чисел:
double z = 10.0 % 4.0; //результат равен 2

27.

Также есть ряд унарных операций, в которых принимает участие один операнд:
++ Операция инкремента.
Инкремент бывает префиксным: ++x - сначала значение переменной x увеличивается на 1, а потом ее
значение возвращается в качестве результата операции.
И также существует постфиксный инкремент: x++ - сначала значение переменной x возвращается в
качестве результата операции, а затем к нему прибавляется 1.
int x1 = 5;
int z1 = ++x1; // z1=6; x1=6
Console.WriteLine($"{x1} - {z1}");
int x2 = 5;
int z2 = x2++; // z2=5; x2=6
Console.WriteLine($"{x2} - {z2}");
-- Операция декремента или уменьшения значения на единицу. Также существует префиксная форма
декремента (--x) и постфиксная (x--).
int x1 = 5;
int z1 = --x1; // z1=4; x1=4
Console.WriteLine($"{x1} - {z1}");
int x2 = 5;
int z2 = x2--; // z2=5; x2=4
Console.WriteLine($"{x2} - {z2}");

28.

Операции присваивания
Как и во многих других языках программирования, в C# имеется базовая
операция присваивания =, которая присвоивает значение правого операнда
левому операнду.
Кроме базовой операции присвоения в C# есть еще ряд операций:
+=: присваивание после сложения. Присваивает левому операнду сумму
левого и правого операндов: выражение A += B равнозначно выражению A
=A+B
-=: присваивание после вычитания. Присваивает левому операнду разность
левого и правого операндов: A -= B эквивалентно A = A - B
*=: присваивание после умножения. Присваивает левому операнду
произведение левого и правого операндов: A *= B эквивалентно A = A * B
/=: присваивание после деления. Присваивает левому операнду частное
левого и правого операндов: A /= B эквивалентно A = A / B
%=: присваивание после деления по модулю. Присваивает левому
операнду остаток от целочисленного деления левого операнда на правый: A
%= B эквивалентно A = A % B

29.

Условные выражения
Отдельный набор операций представляет условные
выражения. Такие операции возвращают логическое
значение, то есть значение типа bool: true, если
выражение истинно, и false, если выражение ложно.
К подобным операциям относятся операции сравнения
и логические операции.

30.

Операции сравнения
В операциях сравнения сравниваются два операнда и возвращается значение типа bool - true, если выражение
верно, и false, если выражение неверно.
== Сравнивает два операнда на равенство. Если они равны, то операция возвращает true, если не равны, то
возвращается false:
int a = 10;
int b = 4;
bool c = a == b; // false
!= Сравнивает два операнда и возвращает true, если операнды не равны, и false, если они равны.
bool c = a != b; // true
< Операция "меньше чем". Возвращает true, если первый операнд меньше второго, и false, если первый
операнд больше второго:
bool c = a < b; // false
> Операция "больше чем". Сравнивает два операнда и возвращает true, если первый операнд больше второго,
иначе возвращает false:
bool c = a > b; // true
<= Операция "меньше или равно". Сравнивает два операнда и возвращает true, если первый операнд меньше
или равен второму. Иначе возвращает false.
bool c = a <= b; // false
>= Операция "больше или равно". Сравнивает два операнда и возвращает true, если первый операнд больше
или равен второму, иначе возвращается false:
bool c = a >= b; // true

31.

Логические операции
Также в C# определены логические операторы, которые также возвращают значение типа bool. В
качестве операндов они принимают значения типа bool. Как правило, применяются к отношениям и
объединяют несколько операций сравнения.
| Операция логического сложения или логическое ИЛИ. Возвращает true, если хотя бы один из
операндов возвращает true.
bool x1 = (5 > 6) | (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается true
& Операция логического умножения или логическое И. Возвращает true, если оба операнда
одновременно равны true.
bool x1 = (5 > 6) & (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается false
|| Операция логического сложения. Возвращает true, если хотя бы один из операндов возвращает true.
bool x1 = (5 > 6) || (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается true
&& Операция логического умножения. Возвращает true, если оба операнда одновременно равны true.
bool x1 = (5 > 6) && (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается false
! Операция логического отрицания. Производится над одним операндом и возвращает true, если
операнд равен false. Если операнд равен true, то операция возвращает false:
bool a = true;
bool b = !a; // false
^ Операция исключающего ИЛИ. Возвращает true, если либо первый, либо второй операнд (но не
одновременно) равны true, иначе возвращает false
bool x5 = (5 > 6) ^ (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается true

32.

Конструкция if..else и
тернарная операция
Условные конструкции - один из базовых компонентов многих языков
программирования, которые направляют работу программы по одному
из путей в зависимости от определенных условий. Одной из таких
конструкций в языке программирования C# является конструкция if..else
Конструкция if/else проверяет истинность некоторого условия и в
зависимости от результатов проверки выполняет определенный код.
Ее простейшая форма состоит из блока if:
if(условие)
{
выполняемые инструкции
}

33.

Выражение else
Блок else выполняется, если условие после if ложно, то есть равно false.
Если блок else содержит только одну инструкцию, то мы можем его
сократить, убрав фигурные скобки:
int num1 = 8;
int num2 = 9;
if(num1 > num2)
Console.WriteLine($"Число {num1} больше числа {num2}");
else
Console.WriteLine($"Число {num1} меньше числа {num2}");

34.

else if
int num1 = 8;
int num2 = 6;
if(num1 > num2)
{
Console.WriteLine($"Число {num1} больше числа {num2}");
}
else if (num1 < num2)
{
Console.WriteLine($"Число {num1} меньше числа {num2}");
}
else
{
Console.WriteLine("Число num1 равно числу num2");
}

35.

Тернарная операция
Тернарную операция также позволяет проверить некоторое условие и в зависимости от его истинности
выполнить некоторые действия. Она имеет следующий синтаксис:
[первый операнд - условие] ? [второй операнд] : [третий операнд]
Здесь сразу три операнда. В зависимости от условия тернарная операция возвращает второй или третий
операнд: если условие равно true, то возвращается второй операнд; если условие равно false, то третий.
Например:
int x=3;
int y=2;
int z = x < y ? (x+y) : (x-y);
Console.WriteLine(z); // 1

36.

Циклы
В C# имеются следующие виды циклов:
for
foreach
while
do...while

37.

Цикл for
Цикл for имеет следующее формальное определение:
for ([действия_до_выполнения_цикла]; [условие]; [действия_после_выполнения])
{
// действия
}
Объявление цикла for состоит из трех частей. Первая часть объявления цикла некоторые действия, которые выполняются один раз до выполнения цикла. Обычно
здесь определяются переменные, которые будут использоваться в цикле.
Вторая часть - условие, при котором будет выполняться цикл. Пока условие равно
true, будет выполняться цикл.
И третья часть - некоторые действия, которые выполняются после завершения блока
цикла. Эти действия выполняются каждый раз при завершении блока цикла.
После объявления цикла в фигурных скобках помещаются сами действия цикла.

38.

Цикл do..while
В цикле do сначала выполняется код цикла, а потом происходит
проверка условия в инструкции while. И пока это условие истинно,
цикл повторяется.
do
{
действия цикла
}
while (условие)

39.

Цикл while
В отличие от цикла do цикл while сразу проверяет истинность
некоторого условия, и если условие истинно, то код цикла
выполняется:
while (условие)
{
действия цикла
}

40.

Цикл foreach
Цикл foreach предназначен для перебора набора или коллекции элементов.
Его общее определение:
foreach(тип_данных переменная in коллекция)
{
// действия цикла
}
После оператора foreach в скобках сначала идет определение переменной.
Затем ключевое слово in и далее коллекция, элементы которой надо
перебрать.
При выполнении цикл последовательно перебирает элементы коллекции и
помещает их в переменную, и таким образом в блоке цикла мы можем
выполнить с ними некоторые действия.

41.

Операторы continue и break
• Иногда возникает ситуация, когда требуется выйти
из цикла, не дожидаясь его завершения. В этом
случае мы можем воспользоваться
оператором break.
• А что если мы хотим, чтобы при проверке цикл не
завершался, а просто пропускал текущую итерацию.
Для этого мы можем воспользоваться
оператором continue.

42.

Массивы
Массив представляет набор однотипных данных. Объявление массива похоже на
объявление переменной за тем исключением, что после указания типа ставятся
квадратные скобки:
тип_переменной[] название_массива;
Например, определим массив целых чисел:
int[] numbers;
После определения переменной массива мы можем присвоить ей определенное
значение:
int[] nums = new int[4];

43.

Индексы и получение элементов
массива
Для обращения к элементам массива используются индексы.
Индекс представляет номер элемента в массиве, при этом
нумерация начинается с нуля, поэтому индекс первого элемента
будет равен 0, индекс четвертого элемента - 3.
Используя индексы, мы можем получить элементы массива:
int[] numbers = { 1, 2, 3, 5 };
// получение элемента массива
Console.WriteLine(numbers[3]); // 5
// получение элемента массива в переменную
var n = numbers[1]; // 2
Console.WriteLine(n); // 2

44.

Свойство Length и длина массива
каждый массив имеет свойство Length, которое хранит длину
массива. Например, получим длину выше созданного массива
numbers:
int[] numbers = { 1, 2, 3, 5 };
Console.WriteLine(numbers.Length); // 4
Для получения длины массива после названия массива через точку
указывается свойство Length: numbers.Length.

45.

Перебор массивов
Для перебора массивов мы можем использовать различные типы циклов. Например, цикл foreach:
int[] numbers = { 1, 2, 3, 4, 5 };
foreach (int i in numbers)
{
Console.WriteLine(i);
}
Здесь в качестве контейнера выступает массив данных типа int. Поэтому мы объявляем переменную с
типом int
Подобные действия мы можем сделать и с помощью цикл for:
int[] numbers = { 1, 2, 3, 4, 5 };
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine(numbers[i]);
}

46.

Многомерные массивы
Массивы характеризуются таким понятием как ранг или количество
измерений. Мы рассматрели массивы, которые имеют одно измерение (то
есть их ранг равен 1) - такие массивы можно представлять в виде ряда (строки
или столбца) элемента. Но массивы также бывают многомерными. У таких
массивов количество измерений (то есть ранг) больше 1.
Массивы которые имеют два измерения (ранг равен 2) называют
двухмерными. Например, создадим одномерный и двухмерный массивы,
которые имеют одинаковые элементы:
int[] nums1 = new int[] { 0, 1, 2, 3, 4, 5 };
int[,] nums2 = { { 0, 1, 2 }, { 3, 4, 5 } };

47.

у каждого массива есть метод
GetUpperBound(номер_размерности), который возвращает
индекс последнего элемента в определенной размерности. И если мы
говорим непосредственно о двухмерном массиве, то первая
размерность (с индексом 0) по сути это и есть таблица.
int[,] numbers = { { 1, 2, 3 }, { 4, 5, 6 }};
int rows = numbers.GetUpperBound(0) + 1; // количество строк
int columns = numbers.Length / rows;
// количество столбцов
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
Console.Write($"{numbers[i, j]} \t");
}
Console.WriteLine();
}

48.

Методы
Если переменные хранят некоторые значения, то методы содержат собой набор
инструкций, которые выполняют определенные действия. По сути метод - это
именованный блок кода, который выполняет некоторые действия.
Общее определение методов выглядит следующим образом:
[модификаторы] тип_возвращаемого_значения название_метода ([параметры])
{
// тело метода
}
Модификаторы и параметры необязательны.
Ранее мы уже использовали как минимум один метод - Console.WriteLine(), который
выводит информацию на консоль.

49.

Определение метода
Определим один метод:
void SayHello()
{
Console.WriteLine("Hello");
}

50.

Вызов методов
Чтобы использовать метод SayHello, нам надо его вызвать. Для вызова метода указывается его имя,
после которого в скобках идут значения для его параметров (если метод принимает параметры).
название_метода (значения_для_параметров_метода);
Например, вызов метода SayHello будет выглядеть следующим образом:
SayHello();
Поскольку метод не принимает никаких параметров, то после названия метода идут пустые скобки.
Объединим определение и вызов метода:
void SayHello()
{
Console.WriteLine("Hello");
}
SayHello(); // Hello
SayHello(); // Hello

51.

Сокращенная запись методов
Если метод в качестве тела определяет только одну инструкцию, то мы
можем сократить определение метода. Например, допустим у нас есть
метод:
void SayHello()
{
Console.WriteLine("Hello");
}
Мы можем его сократить следующим образом:
void SayHello() => Console.WriteLine("Hello");
То есть после списка параметров ставится оператор =>, после которого
идет выполняемая инструкция.

52.

Параметры методов
Параметры позволяют передать в метод некоторые входные данные.
Параметры определяются через заятую в скобках после названия
метода в виде:
тип_метода имя_метода (тип_параметра1 параметр1, тип_параметра2
параметр2, ...)
{
// действия метода
}
Определение параметра состоит из двух частей: сначала идет тип
параметра и затем его имя.

53.

Например, определим метод PrintMessage, который получает извне
выводимое сообщение:
void PrintMessage(string message)
{
Console.WriteLine(message);
}
PrintMessage("Hello work");
// Hello work
PrintMessage("Hello METANIT.COM"); // Hello METANIT.COM
PrintMessage("Hello C#");
// Hello C#
Здесь метод PrintMessage() принимает один параметр, который
называется message и имеет тип string.

54.

Соответствие параметов и
аргументов по типу данных
При передаче значений параметрам важно учитывать тип параметров: между
аргументами и параметрами должно быть соответствие по типу. Например:
void PrintPerson(string name, int age)
{
Console.WriteLine($"Name: {name} Age: {age}");
}
PrintPerson("Tom", 24); // Name: Tom Age: 24
В данном случае первый параметр метода PrintPerson() представляет тип
string, поэтому при вызове метода мы должны передать этому параметру
значение типа string, то есть строку. Второй параметр представляет тип int,
поэтому должны передать ему целое число, которое соответствует типу int.

55.

Необязательные параметры
По умолчанию при вызове метода необходимо предоставить значения для
всех его параметров. Но C# также позволяет использовать необязательные
параметры. Для таких параметров нам необходимо объявить значение по
умолчанию. Также следует учитывать, что после необязательных
параметров все последующие параметры также должны быть
необязательными.
Здесь параметры age и company являются необязательными, так как им присвоены значения.
Поэтому при вызове метода мы можем не передавать для них данные:
void PrintPerson(string name, int age = 1, string company = "Undefined")
{
Console.WriteLine($"Name: {name} Age: {age} Company: {company}");
}
PrintPerson("Tom", 37, "Microsoft"); // Name: Tom Age: 37 Company: Microsoft
PrintPerson("Tom", 37);
// Name: Tom Age: 37 Company: Undefined
PrintPerson("Tom");
// Name: Tom Age: 1 Company: Undefined

56.

Именованные параметры
В предыдущих примерах при вызове методов значения для параметров передавались в
порядке объявления этих параметров в методе. То есть аргументы передавались
параметрам по позиции. Но мы можем нарушить подобный порядок, используя
именованные параметры:
void PrintPerson(string name, int age = 1, string company = "Undefined")
{
Console.WriteLine($"Name: {name} Age: {age} Company: {company}");
}
PrintPerson("Tom", company:"Microsoft", age: 37); // Name: Tom Age: 37 Company:
Microsoft
PrintPerson(age:41, name: "Bob");
// Name: Bob Age: 41 Company: Undefined
PrintPerson(company:"Google", name:"Sam"); // Name: Sam Age: 1 Company: Google
Для передачи значений параметрам о имени при вызове метода указывается имя
параметра и через двоеточие его значение: name:"Tom"

57.

Возвращение значения и
оператор return
Методы могут возвращать некоторое значение. Для этого применяется оператор return, после
которого идет возвращаемое значение:
return возвращаемое значение;
Например, определим метод, который возвращает значение типа string:
string GetMessage()
{
return "Hello";
}
Метод GetMessage имеет тип string, следовательно, он должен возвратить строку. Поэтому в
теле метода используется оператор return, после которого указана возвращаемая строка.
При этом методы, которые в качестве возвращаемого типа имеют любой тип,
кроме void, обязательно должны использовать оператор return для
возвращения значения.

58.

Результат методов, который возвращают значение, мы можем присвоить
переменным или использовать иным образом в программе:
string GetMessage()
{
return "Hello";
}
string message = GetMessage(); // получаем результат метода в переменную
message
Console.WriteLine(message); // Hello
Метод GetMessage() возвращает значение типа string. Поэтому мы можем
присвоить это значение какой-нибудь переменной типа string: string message =
GetMessage();

59.

Сокращенная версия методов с
результатом
Также мы можем сокращать методы, которые возвращают значение:
string GetMessage()
{
return "hello";
}
аналогичен следующему методу:
string GetMessage() => "hello";
А метод
int Sum(int x, int y)
{
return x + y;
}
аналогичен следующему методу:
int Sum(int x, int y) => x + y;

60.

Выход из метода
Оператор return не только возвращает значение, но и производит выход из
метода. Поэтому он должен определяться после остальных инструкций.
Например:
string GetHello()
{
return "Hello";
Console.WriteLine("After return");
}
С точки зрения синтаксиса данный метод корректен, однако его инструкция
Console.WriteLine("After return") не имеет смысла - она никогда не выполнится,
так как до ее выполнения оператор return возвратит значение и произведет
выход из метода.

61.

Передача параметров по
значению
Наиболее простой способ передачи параметров представляет передача по значению,
по сути это обычный способ передачи параметров:
void Increment(int n)
{
n++;
Console.WriteLine($"Число в методе Increment: {n}");
}
Консольный вывод:
Число до метода Increment: 5
Число в методе Increment: 6
Число после метода Increment: 5
int number = 5;
Console.WriteLine($"Число до метода Increment: {number}");
Increment(number);
Console.WriteLine($"Число после метода Increment: {number}");

62.

Передача параметров по
ссылке и модификатор ref
При передаче параметров по ссылке перед параметрами используется модификатор ref:
void Increment(ref int n)
{
n++;
Console.WriteLine($"Число в методе Increment: {n}");
}
int number = 5;
Console.WriteLine($"Число до метода Increment: {number}");
Increment(ref number);
Console.WriteLine($"Число после метода Increment: {number}");
Консольный вывод:
Число до метода Increment: 5
Число в методе Increment: 6
Число после метода Increment: 6

63.

Выходные параметры.
Модификатор out
Выше мы использовали входные параметры. Но параметры могут быть также
выходными. Чтобы сделать параметр выходным, перед ним ставится
модификатор out.
Здесь результат возвращается не через оператор return, а через выходной параметр result.
Использование в программе:
void Sum(int x, int y, out int result)
{
result = x + y;
}
int number;
Sum(10, 15, out number);
Console.WriteLine(number); // 25
Причем, как и в случае с ref ключевое слово out используется как при
определении метода, так и при его вызове.

64.

Входные параметры. Модификатор in
Кроме выходных параметров с модификатором out метод может использовать
входные параметры с модификатором in. Модификатор in указывает, что данный
параметр будет передаваться в метод по ссылке, однако внутри метода его значение
параметра нельзя будет изменить. Например, возьмем следующий метод:
void GetRectangleData(in int width, in int height, out int rectArea, out int rectPerimetr)
{
//width = 25; // нельзя изменить, так как width - входной параметр
rectArea = width * height;
rectPerimetr = (width + height) * 2;
}
int w = 10;
int h = 20;
GetRectangleData(w, h, out var area, out var perimetr);
Console.WriteLine($"Площадь прямоугольника: {area}");
// 200
Console.WriteLine($"Периметр прямоугольника: {perimetr}"); // 60
В данном случае через входные параметры width и height в метод передаются значения, но в самом
методе мы не можем изменить значения этих параметров, так как они определены с модификатором in.

65.

Массив параметров и ключевое слово params
Во всех предыдущих примерах мы использовали постоянное число параметров. Но, используя ключевое
слово params, мы можем передавать неопределенное количество параметров:
void Sum(params int[] numbers)
{
int result = 0;
foreach (var n in numbers)
{
result += n;
}
Console.WriteLine(result);
}
int[] nums = { 1, 2, 3, 4, 5};
Sum(nums);
Sum(1, 2, 3, 4);
Sum(1, 2, 3);
Sum();
Сам параметр с ключевым словом params при определении метода должен представлять одномерный
массив того типа, данные которого мы собираемся использовать. При вызове метода на место параметра
с модификатором params мы можем передать как отдельные значения, так и массив значений, либо
вообще не передавать параметры. Количество передаваемых значений в метод неопределено, однако
все эти значения должны соответствовать типу параметра с params.

66.

Массив в качестве параметра
Также этот способ передачи параметров надо отличать от передачи массива в качестве параметра:
void Sum(int[] numbers, int initialValue)
{
int result = initialValue;
foreach (var n in numbers)
{
result += n;
}
Console.WriteLine(result);
}
int[] nums = { 1, 2, 3, 4, 5};
Sum(nums, 10);
// Sum(1, 2, 3, 4); // так нельзя - нам надо передать массив
Так как метод Sum принимает в качестве параметра массив без ключевого слова params, то при его
вызове нам обязательно надо передать в качестве первого параметра массив. Кроме того, в отличие от
метода с параметром params после параметра-массива могут располагаться другие параметры.

67.

Конструкция switch
Конструкция switch/case оценивает некоторое выражение и сравнивает его значение с набором
значений. И при совпадении значений выполняет определенный код.
switch (выражение)
{
case значение1:
код,выполняемый если выражение имеет значение1
break;
case значение2:
код,выполняемый если выражение имеет значение1
break;
//.............
case значениеN:
код, выполняемый если выражение имеет значениеN
break;
default:
код, выполняемый если выражение не имеет ни одно из выше указанных значений
break;
}

68.

Перечисления enum
Кроме примитивных типов данных в языке
программирования C# есть такой тип
как enum или перечисление. Перечисления
представляют набор логически связанных констант.
enum название_перечисления
{
// значения перечисления
значение1,
значение2,
.......
значениеN
}

69.

Далее в программе мы можем использовать подобные переменные/константы/параметры
как и любые другие:
DayTime dayTime = DayTime.Morning;
if(dayTime == DayTime.Morning)
Console.WriteLine("Доброе утро");
else
Console.WriteLine("Привет");
enum DayTime
{
Morning,
Afternoon,
Evening,
Night
}

70.

Тип и значения констант
перечисления
Константы перечисления могут иметь тип. Тип указывается после названия
перечисления через двоеточие:
enum Time : byte
{
Morning,
Afternoon,
Evening,
Night
}
Тип перечисления обязательно должен представлять целочисленный тип (byte, sbyte,
short, ushort, int, uint, long, ulong). Если тип явным образом не указан, то по
умолчанию используется тип int.

71.

Классы и объекты
По сути класс представляет новый тип, который определяется пользователем. Класс
определяется с помощью ключевого слова сlass:
class название_класса
{
// содержимое класса
}
После слова class идет имя класса и далее в фигурных скобках идет собственно
содержимое класса. Например, определим в файле Program.cs класс Person, который
будет представлять человека:
class Person
{
}

72.

Поля и методы класса
Класс может хранить некоторые данные. Для хранения данных в классе применяются
поля. По сути поля класса - это переменные, определенные на уровне класса.
Кроме того, класс может определять некоторое поведение или выполняемые
действия. Для определения поведения в классе применяются методы.
Итак, добавим в класс Person поля и методы:
class Person
{
public string name = "Undefined"; // имя
public int age;
// возраст
public void Print()
{
Console.WriteLine($"Имя: {name} Возраст: {age}");
}
}

73.

Создание объекта класса
После определения класса мы можем создавать его объекты. Для
создания объекта применяются конструкторы. По сути конструкторы
представляют специальные методы, которые называются так же как и
класс, и которые вызываются при создании нового объекта класса и
выполняют инициализацию объекта. Общий синтаксис вызова
конструктора:
new конструктор_класса(параметры_конструктора);
Сначала идет оператор new, который выделяет память для объекта, а
после него идет вызов конструктора.

74.

Конструктор по умолчанию
Если в классе не определено ни одного конструктора (как в случае с нашим классом Person), то для этого
класса автоматически создается пустой конструктор по умолчанию, который не принимает никаких
параметров.
Теперь создадим объект класса Person:
Person tom = new Person(); // создание объекта класса Person
// определение класса Person
class Person
{
public string name = "Undefined";
public int age;
public void Print()
{
Console.WriteLine($"Имя: {name} Возраст: {age}");
}
}

75.

Обращение к функциональности
класса
Для обращения к функциональности класса - полям, методам (а также другим элементам класса)
применяется точечная нотация точки - после объекта класса ставится точка, а затем элемент класса:
объект.поле_класса
объект.метод_класса(параметры_метода)
Например, обратимся к полям и методам объекта Person:
Person tom = new Person(); // создание объекта класса Person
// Получаем значение полей в переменные
string personName = tom.name;
int personAge = tom.age;
Console.WriteLine($"Имя: {personName} Возраст {personAge}"); // Имя: Undefined Возраст: 0
// устанавливаем новые значения полей
tom.name = "Tom";
tom.age = 37;
// обращаемся к методу Print
tom.Print(); // Имя: Tom Возраст: 37

76.

Добавление класса в Visual Studio

77.

78.

class Person
{
public string name = "Undefined";
public void Print()
{
Console.WriteLine($"Person {name}");
}
}

79.

Person tom = new Person();
tom.name = "Tom";
tom.Print();
// Person Tom

80.

Создание конструкторов
Person tom = new Person();
// Создание объекта класса Person
tom.Print();
Возраст: 37
// Имя: Tom
class Person
{
public string name;
public int age;
public Person()
{
Console.WriteLine("Создание объекта Person");
name = "Tom";
age = 37;
}
public void Print()
{
Console.WriteLine($"Имя: {name} Возраст: {age}");
}
}

81.

Person tom = new Person();
// вызов 1-ого конструктора без параметров
Person bob = new Person("Bob");
//вызов 2-ого конструктора с одним параметром
Person sam = new Person("Sam", 25); // вызов 3-его конструктора с двумя параметрами
tom.Print();
bob.Print();
sam.Print();
// Имя: Неизвестно Возраст: 18
// Имя: Bob Возраст: 18
// Имя: Sam Возраст: 25
class Person
{
public string name;
public int age;
public Person() { name = "Неизвестно"; age = 18; }
public Person(string n) { name = n; age = 18; }
public Person(string n, int a) { name = n; age = a; }
public void Print()
{
Console.WriteLine($"Имя: {name}
}
}
Возраст: {age}");
// 1 конструктор
// 2 конструктор
// 3 конструктор

82.

Ключевое слово this
Person sam = new("Sam", 25);
sam.Print();
// Имя: Sam
Возраст: 25
class Person
{
public string name;
public int age;
public Person() { name = "Неизвестно"; age = 18; }
public Person(string name) { this.name = name; age = 18; }
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
public void Print() => Console.WriteLine($"Имя: {name} Возраст: {age}");
}

83.

Инициализаторы объектов
Для инициализации объектов классов можно применять инициализаторы. Инициализаторы
представляют передачу в фигурных скобках значений доступным полям и свойствам объекта:
Person tom = new Person { name = "Tom", age = 31 };
// или так
// Person tom = new() { name = "Tom", age = 31 };
tom.Print();
// Имя: Tom Возраст: 31
С помощью инициализатора объектов можно присваивать значения всем доступным полям и
свойствам объекта в момент создания. При использовании инициализаторов следует
учитывать следующие моменты:
• С помощью инициализатора мы можем установить значения только доступных из вне
класса полей и свойств объекта. Например, в примере выше поля name и age имеют
модификатор доступа public, поэтому они доступны из любой части программы.
• Инициализатор выполняется после конструктора, поэтому если и в конструкторе, и в
инициализаторе устанавливаются значения одних и тех же полей и свойств, то значения,
устанавливаемые в конструкторе, заменяются значениями из инициализатора.

84.

Деконструкторы
class Person
{
string name;
int age;
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
Person person = new Person("Tom", 33);
(string name, int age) = person;
Console.WriteLine(name);
Console.WriteLine(age);
public void Deconstruct(out string personName, out int personAge)
{
personName = name;
personAge = age;
}
}
// Tom
// 33

85.

Класс Program и метод Main.
Точкой входа в программу на языке C# является метод Main. Именно с этого
метода начинается выполнение программы на C#. И программа на C# должна
обязательно иметь метод Main. Однако может возникнуть вопрос, какой еще
метод Main, если, например, Visual Studio 2022 по умолчанию создает проект
консольного приложения со следующим кодом:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
И эта программа никаких методов Main не содержит, но при этом нормально
выполняется и выводит на консоль строку "Hello, World!", как и
запланировано. Это так называемая программа верхнего уровня (top-level
program). А вызов Console.WriteLine("Hello, World!") представляет инструкцию
вехнего уровня (top-level statement)

86.

Поэтому выше представленный код фактически эквивалентен
следующей программе:
class Program
{
static void Main(string[] args)
{
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
}
}

87.

Определение структуры
Для определения структуры применяется ключевое
слово struct:
struct имя_структуры
{
// элементы структуры
}

88.

struct Person
{
public string name;
public int age;
public void Print()
{
Console.WriteLine($"Имя: {name} Возраст: {age}");
}
}

89.

Создание объекта структуры
Инициализация с помощью конструктора
Для использования структуры ее необходмо инициализировать.
Для инициализации создания объектов структуры, как и в случае с
классами, применяется вызов конструктура с оператором new.
Даже если в коде стуктуры не определено ни одного конструктора,
тем не менее имеет как минимум один конструктор - конструктор
по умолчанию, который генерируется компилятором. Этот
конструктор не принимает параметров и создает объект структуры
со значениями по умолчанию.
new название_структуры();

90.

Person tom = new Person(); // вызов конструктора
// или так
// Person tom = new();
tom.name = "Tom"; // изменяем значение по умолчанию в поле name
tom.Print(); // Имя: Tom Возраст: 0
struct Person
{
public string name;
public int age;
public void Print()
{
Console.WriteLine($"Имя: {name} Возраст: {age}");
}
}

91.

Непосредственная
иницилизация полей
Person tom;
// не вызываем конструктор
// инициализация полей
tom.name = "Sam";
tom.age = 37;
tom.Print(); // Имя: Sam Возраст: 37
struct Person
{
public string name;
public int age;
public void Print()
{
Console.WriteLine($"Имя: {name} Возраст: {age}");
}
}

92.

Инициализация полей по
умолчанию
Person tom = new Person();
tom.Print(); // Имя:Tom Возраст: 1
struct Person
{
// инициализация полей значениями по умолчанию - доступна с C#10
public string name = "Tom";
public int age = 1;
public void Print() =>Console.WriteLine($"Имя: {name} Возраст: {age}");
}

93.

Конструкторы структуры
Person tom = new();
Person bob = new("Bob");
Person sam = new("Sam", 25);
tom.Print(); // !!!! Имя: Возраст: 0
bob.Print(); // Имя: Bob Возраст: 1
sam.Print(); // Имя: Sam Возраст: 25
struct Person
{
public string name;
public int age;
public Person(string name = "Tom", int age = 1)
{
this.name = name;
this.age = age;
}
public void Print() => Console.WriteLine($"Имя: {name} Возраст: {age}");
}

94.

Инициализатор структуры
Person tom = new Person { name = "Tom", age = 22 };
tom.Print(); // Имя: Tom Возраст: 22
struct Person
{
public string name;
public int age;
public void Print() => Console.WriteLine($"Имя: {name} Возраст: {age}");
}

95.

Копирование структуры с
помощью with
Person tom = new Person { name = "Tom", age = 22 };
Person bob = tom with { name = "Bob" };
bob.Print(); // Имя: Bob Возраст: 22

96.

Типы значений и ссылочные типы
Типы значений:
Целочисленные типы (byte, sbyte, short, ushort, int, uint, long, ulong)
Типы с плавающей запятой (float, double)
Тип decimal
Тип bool
Тип char
Перечисления enum
Структуры (struct)

97.

Ссылочные типы:
Тип object
Тип string
Классы (class)
Интерфейсы (interface)
Делегаты (delegate)

98.

class Program
{
static void Main(string[] args)
{
Calculate(5);
}
static void Calculate(int t)
{
int x = 6;
int y = 7;
int z = y + t;
}
}

99.

static void Calculate(int t)
{
object x = 6;
int y = 7;
int z = y + t;
}

100.

Составные типы
State state1 = new State(); // State - структура, ее данные размещены в стеке
Country country1 = new Country(); // Country - класс, в стек помещается ссылка
на адрес в хипе
// а в хипе располагаются все данные объекта country1
struct State
{
public int x;
public int y;
}
class Country
{
public int x;
public int y;
}

101.

State state1 = new State();
Country country1 = new Country();
struct State
{
public int x;
public int y;
public Country country = new();
}
class Country
{
public int x;
public int y;
}

102.

Копирование значений
State state1 = new State(); // Структура State
State state2 = new State();
state2.x = 1;
state2.y = 2;
state1 = state2;
state2.x = 5; // state1.x=1 по-прежнему
Console.WriteLine(state1.x); // 1
Console.WriteLine(state2.x); // 5
Country country1 = new Country(); // Класс Country
Country country2 = new Country();
country2.x = 1;
country2.y = 4;
country1 = country2;
country2.x = 7; // теперь и country1.x = 7, так как обе ссылки и country1 и country2
// указывают на один объект в хипе
Console.WriteLine(country1.x); // 7
Console.WriteLine(country2.x); // 7

103.

Ссылочные типы внутри типов значений
State state1 = new State();
State state2 = new State();
state2.country = new Country();
state2.country.x = 5;
state1 = state2;
state2.country.x = 8; // теперь и state1.country.x=8, так как state1.country и state2.country
// указывают на один объект в хипе
Console.WriteLine(state1.country.x); // 8
Console.WriteLine(state2.country.x); // 8
struct State
{
public int x;
public int y;
public Country country = new(); // выделение памяти для объекта Country
}
class Country
{
public int x;
public int y;
}

104.

Область видимости (контекст)
переменных и констант
Существуют различные контексты:
Контекст класса. Переменные, определенные на уровне
класса, доступны в любом методе этого класса. Их еще
называют глобальными переменными или полями
Контекст метода. Переменные, определенные на уровне
метода, являются локальными и доступны только в рамках
данного метода. В других методах они недоступны
Контекст блока кода. Переменные, определенные на
уровне блока кода, также являются локальными и
доступны только в рамках данного блока. Вне своего
блока кода они не доступны.

105.

Person tom = new();
tom.PrintName();
tom.PrintSurname();
class Person
// начало контекста класса
{
string type = "Person";
// переменная уровня класса
public void PrintName()
// начало контекста метода PrintName
{
string name = "Tom";
{
// переменная уровня метода
// начало контекста блока кода
string shortName = "Tomas"; // переменная уровня блока кода
Console.WriteLine(type); // в блоке доступна переменная
класса
Console.WriteLine(name); // в блоке доступна переменная
окружающего метода
Console.WriteLine(shortName);// в блоке доступна переменная
этого же блока
}
// конец контекста блока кода, переменная
shortName уничтожается
Console.WriteLine(type);
класса
// в методе доступна переменная
Console.WriteLine(name);
// в методе доступна переменная этого
же метода
//Console.WriteLine(shortName); //так нельзя, переменная c
определена в блоке кода
//Console.WriteLine(surname); //так нельзя, переменная
surname определена в другом методе
}
// конец контекста метода PrintName, переменная name
уничтожается
public void PrintSurname() // начало контекста метода
PrintSurname
{
string surname = "Smith"; // переменная уровня метода
Console.WriteLine(type);
// в методе доступна переменная
класса
Console.WriteLine(surname); // в методе доступна переменная
этого же метода
}
// конец конекста метода PrintSurname, переменная surname
уничтожается
} // конец контекста класса, переменная type уничтожается

106.

class Person
{
string name = "Tom";
// переменная уровня класса
public void PrintName()
{
string name = "Tomas"; // переменная уровня метода скрывает
переменную уровня класса
Console.WriteLine(name); // Tomas
}
}

107.

Пространства имен
Обычно определяемые классы и другие типы в .NET не
существуют сами по себе, а заключаются в специальные
контейнеры - пространства имен. Пространства имен
позволяют организовать код программы в логические блоки,
поволяют объединить и отделить от остального кода некоторую
функциональность, которая связана некоторой общей идеей
или которая выполняет определенную задачу.
Для определения пространства имен применяется ключевое
слово namespace, после которого идет название название
пространства имен:
namespace имя_пространства_имен
{
// содержимое пространства имен
}

108.

namespace Base
{
class Person
{
string name;
public Person(string name) => this.name = name;
public void Print() => Console.WriteLine($"Имя: {name}");
}
}

109.

Base.Person tom = new("Tom");
Person tom = new Person("Tom"); // Ошибка - Visual Studio не видит класс Person
tom.Print();
namespace Base
{
class Person
{
string name;
public Person(string name) => this.name = name;
public void Print() => Console.WriteLine($"Имя: {name}");
}
}

110.

Подключение пространства
имен
using Base; // подключение пространства имен Base
Person tom = new("Tom");
tom.Print(); // Имя: Tom
namespace Base
{
class Person
{
string name;
public Person(string name) => this.name = name;
public void Print() => Console.WriteLine($"Имя: {name}");
}
}

111.

Вложенные пространства имен
namespace Base {
namespace PersonTypes {
class Person {
string name;
OrganisationTypes.Company company;
public Person(string name, OrganisationTypes.Company company) {
this.name = name;
this.company = company;
}
public void Print() {
Console.WriteLine($"Имя: {name} ");
company.Print();
}}}
namespace OrganisationTypes {
class Company {
string title;
public Company(string title) => this.title = title;
public void Print() => Console.WriteLine($"Название компании: {title}");
} } }

112.

Глобальные пространства имен

113.

В файле Base.cs определяется класс Company в пространстве Base:
namespace Base;
class Company
{
string title;
public Company(string title) => this.title = title;
public void Print() => Console.WriteLine($"Компания: {title}");
}

114.

В файле Person.cs определен класс Person, который использует класс Company:
using Base;
class Person
{
string name;
Company company;
public Person(string name, Company company)
{
this.name = name;
this.company = company;
}
public void Print()
{
Console.WriteLine($"Имя: {name}");
company.Print();
}
}

115.

Чтобы использовать класс Company мы подключаем его
пространство имен: using Base;
В классе Program.cs используются классы Person и Company:
using Base;
Company microsoft = new("Microsoft");
Person tom = new("Tom", microsoft);
tom.Print();

116.

global using подключаемое_пространство_имен;
Например, изменим файл Program.cs следующим образом:
global using Base;
Company microsoft = new("Microsoft");
Person tom = new("Tom", microsoft);
tom.Print();
Теперь пространство Base подключается во все файлы кода в проекте. И
из файла Person.cs мы можем убрать строку
using Base;

117.

Определение глобальных
пространств в отдельном файле
global using System.Text;
global using System.Reflection;
global using Base;

118.

Создание библиотеки классов

119.

120.

121.

public class Person
{
string name;
public Person(string name)
{
this.name = name;
}
public void Print() => Console.WriteLine($"Name:
{name}");
}

122.

123.

124.

125.

using MyLib; // подключение пространства имен из библиотеки классов
Person tom = new("Tom");
tom.Print(); // Name: Tom

126.

Модификаторы доступа
В языке C# применяются следующие модификаторы доступа:
private: закрытый или приватный компонент класса или структуры. Приватный
компонент доступен только в рамках своего класса или структуры.
private protected: компонент класса доступен из любого места в своем классе или
в производных классах, которые определены в той же сборке.
protected: такой компонент класса доступен из любого места в своем классе или в
производных классах. При этом производные классы могут располагаться в других
сборках.
internal: компоненты класса или структуры доступен из любого места кода в той
же сборке, однако он недоступен для других программ и сборок.
protected internal: совмещает функционал двух модификаторов protected и
internal. Такой компонент класса доступен из любого места в текущей сборке и из
производных классов, которые могут располагаться в других сборках.
public: публичный, общедоступный компонент класса или структуры. Такой
компонент доступен из любого места в коде, а также из других программ и
сборок.

127.

128.

public class Person
{
string name;
public Person(string name)
{
this.name = name;
}
public void Print() =>
Console.WriteLine($"Name: {name}");
}

129.

Модификаторы в рамках
текущего проекта
class State
{
// все равно, что private string defaultVar;
string defaultVar ="default";
// поле доступно только из текущего класса
private string privateVar = "private";
// доступно из текущего класса и производных классов, которые определены в этом же проекте
protected private string protectedPrivateVar = "protected private";
// доступно из текущего класса и производных классов
protected string protectedVar = "protected";
// доступно в любом месте текущего проекта
internal string internalVar = "internal";
// доступно в любом месте текущего проекта и из классов-наследников в других проектах
protected internal string protectedInternalVar = "protected internal";
// доступно в любом месте программы, а также для других программ и сборок
public string publicVar = "public";

130.

// по умолчанию имеет модификатор private
void Print() => Console.WriteLine(defaultVar);
// метод доступен только из текущего класса
private void PrintPrivate() => Console.WriteLine(privateVar);
// доступен из текущего класса и производных классов, которые определены в этом же
проекте
protected private void PrintProtectedPrivate() => Console.WriteLine(protectedPrivateVar);
// доступен из текущего класса и производных классов
protected void PrintProtected() => Console.WriteLine(protectedVar);
// доступен в любом месте текущего проекта
internal void PrintInternal() => Console.WriteLine(internalVar);
// доступен в любом месте текущего проекта и из классов-наследников в других
проектах
protected internal void PrintProtectedInternal() => Console.WriteLine(protectedInternalVar);
}
// доступен в любом месте программы, а также для других программ и сборок
public void PrintPublic() => Console.WriteLine(publicVar);

131.

class StateConsumer
{
public void PrintState()
{
State state = new State();
// обратиться к переменной defaultVar у нас не получится, так как она имеет модификатор private и класс StateConsumer ее не видит
Console.WriteLine(state.defaultVar); //Ошибка, получить доступ нельзя
// то же самое относится и к переменной privateVar
Console.WriteLine(state.privateVar); // Ошибка, получить доступ нельзя
// обратиться к переменной protectedPrivateVar не получится, так как класс StateConsumer не является классом-наследником класса State
Console.WriteLine(state.protectedPrivateVar); // Ошибка, получить доступ нельзя
// обратиться к переменной protectedVar тоже не получится, так как класс StateConsumer не является классом-наследником класса State
Console.WriteLine(state.protectedVar); // Ошибка, получить доступ нельзя
// переменная internalVar с модификатором internal доступна из любого места текущего проекта, поэтому спокойно присваиваем ей
значение
Console.WriteLine(state.internalVar);
// переменная protectedInternalVar так же доступна из любого места текущего проекта
Console.WriteLine(state.protectedInternalVar);
// переменная publicVar общедоступна
Console.WriteLine(state.publicVar);
}
}

132.

class StateConsumer
{
public void PrintState()
{
State state = new State();
state.Print(); //Ошибка, получить доступ нельзя
state.PrintPrivate(); // Ошибка, получить доступ нельзя
state.PrintProtectedPrivate(); // Ошибка, получить доступ нельзя
state.PrintProtected(); // Ошибка, получить доступ нельзя
state.PrintInternal(); // норм
state.PrintProtectedInternal(); // норм
state.PrintPublic();
}
}
// норм

133.

Определение свойств
[модификаторы] тип_свойства название_свойства
{
get { действия, выполняемые при получении значения свойства}
set { действия, выполняемые при установке значения свойства}
}

134.

Person person = new Person();
// Устанавливаем свойство - срабатывает блок Set, значение "Tom" и есть передаваемое в свойство value
person.Name = "Tom";
// Получаем значение свойства и присваиваем его переменной - срабатывает блок Get
string personName = person.Name;
Console.WriteLine(personName); // Tom
class Person
{
private string name = "Undefined";
public string Name
{
get
{
return name; // возвращаем значение свойства
}
set
{
name = value; // устанавливаем новое значение свойства
}}}

135.

Свойства только для чтения и записи
Person person = new Person();
// свойство для чтения - можно получить значение
Console.WriteLine(person.Name); // Tom
// но нельзя установить
// person.Name = "Bob"; // ! Ошибка
// свойство для записи - можно устновить значение
person.Age = 37;
// но нелзя получить
// Console.WriteLine(person.Age); // ! Ошибка
person.Print();
class Person
{
string name = "Tom";
int age = 1;
// свойство только для записи
public int Age
{
set { age = value; }
}
// свойство только для чтения
public string Name
{
get { return name; }
}
public void Print()=> Console.WriteLine($"Name: {name} Age: {age}");
}

136.

Вычисляемые свойства
Person tom = new("Tom", "Smith");
Console.WriteLine(tom.Name); // Tom Smith
class Person
{
string firstName;
string lastName;
public string Name
{
get { return $"{firstName} {lastName}"; }
}
public Person(string firstName, string lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
}

137.

Модификаторы доступа
Person tom = new("Tom");
// Ошибка - set объявлен с модификатором private
//tom.Name = "Bob";
Console.WriteLine(tom.Name); // Tom
class Person
{
string name = "";
public string Name
{
get { return name; }
private set { name = value; }
}
public Person(string name) => Name = name;
}

138.

Автоматические свойства
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}

139.

Person tom = new();
Console.WriteLine(tom.Name); // Tom
Console.WriteLine(tom.Age); // 37
class Person
{
public string Name { get; set; } = "Tom";
public int Age { get; set; } = 37;
}

140.

Блок init
Person person = new();
//person.Name = "Bob"; //! Ошибка - после инициализации
изменить значение нельзя
Console.WriteLine(person.Name); // Undefined
public class Person
{
public string Name { get; init; } = "Undefined";
}

141.

Второй способ - через конструктор:
Person person = new("Tom");
Console.WriteLine(person.Name); // Tom
public class Person
{
public Person(string name) => Name = name;
public string Name { get; init; }
}
Третий способ - через инициализатор:
Person person = new() { Name = "Bob"};
Console.WriteLine(person.Name); // Bob
public class Person
{
public string Name { get; init; } = "";
}

142.

var person = new Person() { Name = "Sam" };
Console.WriteLine(person.Name); // Sam
Console.WriteLine(person.Email); // Sam@gmail.com
public class Person
{
string name = "";
public string Name
{
get { return name; }
init
{
name = value;
Email = $"{value}@gmail.com";
}
}
public string Email { get; init; } = "";
}

143.

Сокращенная запись свойств
Как и методы, мы можем сокращать определения свойств. Поскольку блоки get и set представляют специальные методы, то как и
обычные методы, если они содержат одну инструкцию, то мы их можем сократить с помощью оператора =>:
class Person
{
string name;
public string Name
{
get => name;
set => name = value;
}
}
Также можно сокращать все свойство в целом:
class Person
{
string name;
// эквивалентно public string Name { get { return name; } }
public string Name => name;
}

144.

Перегрузка методов
И в языке C# мы можем создавать в классе несколько
методов с одним и тем же именем, но разной
сигнатурой. Что такое
сигнатура? Сигнатура складывается из следующих
аспектов:
• Имя метода
• Количество параметров
• Типы параметров
• Порядок параметров
• Модификаторы параметров

145.

public int Sum(int x, int y)
{
return x + y;
}
У данного метода сигнатура будет выглядеть так: Sum(int,
int)
И перегрузка метода как раз заключается в том, что методы
имеют разную сигнатуру, в которой совпадает только название
метода. То есть методы должны отличаться по:
•Количеству параметров
•Типу параметров
•Порядку параметров
•Модификаторам параметров

146.

class Calculator
{
public void Add(int a, int b)
{
int result = a + b;
Console.WriteLine($"Result is {result}");
}
public void Add(int a, int b, int c)
{
int result = a + b + c;
Console.WriteLine($"Result is {result}");
}
public int Add(int a, int b, int c, int d)
{
int result = a + b + c + d;
Console.WriteLine($"Result is {result}");
return result;
}
public void Add(double a, double b)
{
double result = a + b;
Console.WriteLine($"Result is {result}");
}
}

147.

То есть мы можем представить сигнатуры данных методов следующим
образом:
Add(int, int)
Add(int, int, int)
Add(int, int, int, int)
Add(double, double)
После определения перегруженных версий мы можем использовать их в
программе:
Calculator calc = new Calculator();
calc.Add(1, 2); // 3
calc.Add(1, 2, 3); // 6
calc.Add(1, 2, 3, 4); // 10
calc.Add(1.4, 2.5); // 3.9

148.

void Increment(ref int val)
{
val++;
Console.WriteLine(val);
}
void Increment(int val)
{
val++;
Console.WriteLine(val);
}

149.

int Sum(int x, int y)
{
return x + y;
}
int Sum(int number1, int number2)
{
return number1 + number2;
}
void Sum(int x, int y)
{
Console.WriteLine(x + y);
}

150.

Статические поля
Person bob = new(68);
bob.СheckAge(); // Уже на пенсии
Person tom = new(37);
tom.СheckAge(); // Сколько лет осталось до пенсии: 28
// получение статического поля
Console.WriteLine(Person.retirementAge); // 65
// изменение статического поля
Person.retirementAge = 67;
class Person
{
int age;
public static int retirementAge = 65;
public Person(int age)
{
this.age = age;
}
public void СheckAge()
{
if (age >= retirementAge)
Console.WriteLine("Уже на пенсии");
else
Console.WriteLine($"Сколько лет осталось до пенсии: {retirementAge - age}");
}
}

151.

152.

Статические свойства
Person bob = new(68);
bob.СheckAge();
Console.WriteLine(Person.RetirementAge); // 65
class Person
{
int age;
static int retirementAge = 65;
public static int RetirementAge
{
get { return retirementAge; }
set { if (value > 1 && value < 100) retirementAge = value; }
}
public Person(int age)
{
this.age = age;
}
public void СheckAge()
{
if (age >= retirementAge)
Console.WriteLine("Уже на пенсии");
else
Console.WriteLine($"Сколько лет осталось до пенсии: {retirementAge - age}") ;
}
}

153.

var tom = new Person();
var bob = new Person();
var sam = new Person();
Console.WriteLine(Person.Counter); // 3
class Person
{
static int counter = 0;
public static int Counter => counter;
public Person()
{
counter++;
}
}

154.

Статические методы
Person bob = new(68);
Person.CheckRetirementStatus(bob);
class Person
{
public int Age { get; set; }
static int retirementAge = 65;
public Person(int age) => Age = age;
public static void CheckRetirementStatus(Person person)
{
if (person.Age >= retirementAge)
Console.WriteLine("Уже на пенсии");
else
Console.WriteLine($"Сколько лет осталось до пенсии: {retirementAge - person.Age}") ;
}
}

155.

Статический конструктор
Кроме обычных конструкторов у класса также могут быть
статические конструкторы. Статические конструкторы
имеют следующие отличительные черты:
• Статические конструкторы не должны иметь
модификатор доступа и не принимают параметров
• Как и в статических методах, в статических
конструкторах нельзя использовать ключевое слово this
для ссылки на текущий объект класса и можно
обращаться только к статическим членам класса
• Статические конструкторы нельзя вызвать в программе
вручную. Они выполняются автоматически при самом
первом создании объекта данного класса или при первом
обращении к его статическим членам (если таковые
имеются)

156.

Console.WriteLine(Person.RetirementAge);
class Person
{
static int retirementAge;
public static int RetirementAge => retirementAge;
static Person()
{
if (DateTime.Now.Year == 2022)
retirementAge = 65;
else
retirementAge = 67;
}
}

157.

Статические классы
Console.WriteLine(Operations.Add(5, 4));
// 9
Console.WriteLine(Operations.Subtract(5, 4)); // 1
Console.WriteLine(Operations.Multiply(5, 4)); // 20
static class Operations
{
public static int Add(int x, int y) => x + y;
public static int Subtract(int x, int y) => x - y;
public static int Multiply(int x, int y) => x * y;
}

158.

Поля для чтения и модификатор readonly
Person tom = new Person("Tom");
Console.WriteLine(tom.name);
//tom.name = "Sam"; // !Ошибка: нельзя изменить
class Person
{
public readonly string name = "Undefined"; // можно так инициализировать
public Person(string name)
{
this.name = name; // в конструкторе также можно присвоить значение полю для чтения
}
public void ChangeName(string otherName)
{
//this.name = otherName; // так нельзя
}
}

159.

Сравнение констант
• Константы должны быть определены во время
компиляции, а поля для чтения могут быть
определены во время выполнения программы.
Соответственно значение константы можно установить
только при ее определении.
Поле для чтения можно инициализировать либо при
его определении, либо в конструкторе класса.
• Константы не могут использовать модификатор static,
так как уже неявно являются статическими. Поля для
чтения могут быть как статическими, так и не
статическими

160.

Структуры для чтения
readonly struct Person { }
Особенностью таких структур является то, что все их поля должны быть также полями для чтения:
readonly struct Person
{
public readonly string name;
public Person(string name)
{
this.name = name;
}}
То же самое касается и свойств, которые должны быть доступны только для чтения:
readonly struct Person
{
public readonly string Name { get; } // указывать readonly необязательно
public int Age { get; } // свойство только для чтения
public Person(string name, int age)
{
Name = name;
Age = age;
}}

161.

Null и ссылочные типы
string name = null;
Console.WriteLine(name);
_________________________________________________
string? name = null;
Console.WriteLine(name); // ничего не выведет
_________________________________________________
string? name = Console.ReadLine();

162.

При этом подобные ссылочные типы, которые допускают
присвоение значения null, доступно только в nullable-контексте.
Для nullable-контекста характерны следующие особенности:
• Переменную ссылочного типа следует инициализировать
конкретным значением, ей не следует присваивать значение null
• Переменной ссылочного nullable-типа можно присвоить значение
null, но перед использование необходимо проверять ее на
значение null.

163.

164.

Оператор ! (null-forgiving operator)
Оператор ! (null-forgiving operator) позволяет указать, что переменная
ссылочного типа не равна null:
string? name = null;
PrintUpper(name!);
void PrintUpper(string text)
{
if(text == null) Console.WriteLine("null");
else Console.WriteLine(text.ToUpper());
}

165.

Null и значимые типы
int? val = null;
IsNull(val); // null
val = 22;
IsNull(val); // 22
void IsNull(int? obj)
{
if (obj == null) Console.WriteLine("null");
else Console.WriteLine(obj);
}

166.

Однако если переменная/параметр представляет значимый не nullableтип, то присвоить им значение null не получится:
int val = null; // ! ошибка, переменная val НЕ представляет тип nullable
Стоит отметить, что фактически запись ? для значимых типов является
упрощенной формой использования структуры System.Nullable<T>.
Параметр T в угловых скобках представляет универсальный параметр,
вместо которого в программе подставляется конкретный тип данных.
Следующие виды определения переменных будут эквивалентны:
int? number1 = 5;
Nullable<int> number2 = 5;

167.

Свойства Value и HasValue и
метод GetValueOrDefault
Структура Nullable<T> имеет два свойства:
Value - значение объекта
HasValue: возвращает true, если объект хранит некоторое
значение, и false, если объект равен null.

168.

PrintNullable(5);
// 5
PrintNullable(null); // параметр равен null
void PrintNullable(int? number)
{
if (number.HasValue)
{
Console.WriteLine(number.Value);
// аналогично
Console.WriteLine(number);
}
else
{
Console.WriteLine("параметр равен null");
}
}

169.

Однако если мы попробуем получить через свойство Value значение переменной, которая
равна null, то мы столкнемся с ошибкой:
int? number = null;
Console.WriteLine(number.Value); // ! Ошибка
Console.WriteLine(number);
// Ошибки нет - просто ничего не выведет
Также структура Nullable<T> имеет метод GetValueOrDefault(). Он возвращает значение
переменной/параметра, если они не равны null. Если они равны null, то возвращается
значение по умолчанию. Значение по умолчанию можно передать в метод. Если в метод не
передается данных, то возвращается значение по умолчанию для данного типа данных
(например, для числовых данных это число 0).
int? number = null; // если значения нет, метод возвращает значение по умолчанию
Console.WriteLine(number.GetValueOrDefault()); // 0 - значение по умолчанию для числовых
типов
Console.WriteLine(number.GetValueOrDefault(10)); // 10
number = 15; // если значение задано, оно возвращается методом
Console.WriteLine(number.GetValueOrDefault()); // 15
Console.WriteLine(number.GetValueOrDefault(10)); // 15

170.

Преобразование значимых nullable-типов
Рассмотрим возможные преобразования:
явное преобразование от T? к T
int? x1 = null;
if(x1.HasValue)
{
int x2 = (int)x1;
Console.WriteLine(x2);
}
неявное преобразование от T к T?
int x1 = 4;
int? x2 = x1;
Console.WriteLine(x2);
неявные расширяющие преобразования от V к T?
int x1 = 4;
long? x2 = x1;
Console.WriteLine(x2);+994++++

171.

явные сужающие преобразования от V к T?
long x1 = 4;
int? x2 = (int?)x1;
Подобным образом работают явные сужающие преобразования от V? к
T?
long? x1 = 4;
int? x2 = (int?)x1;
явные сужающие преобразования от V? к T
long? x1 = null;
if (x1.HasValue)
{
int x2 = (int)x1;
}

172.

Операции с nullable-типами
nullable-типы поддерживают тот же набор операций, что и их не-nullable
двойники. Но следует учитывать, что если в операции участвует nullable-тип, то
результатом также будет значение nullable-типа
int? x = 5;
int z = x + 7;
// нельзя
int? w = x + 7;
// можно
int d = x.Value + 7; // можно
В арифметических операциях, если один из операндов равен null, то
результатом операции также будет null:
int? x = null;
int? w = x + 7;
// w = null
В операциях сравнения, если хотя бы один из операндов равен null, то
возвращается false

173.

Проверка на null. Null guard
void PrintUpper(string? text)
{
if (text!=null)
{
Console.WriteLine(text.ToUpper());
}
}

174.

Кроме того, с помощью оператора is мы можем проверить значение объекта:
объект is значение
Если объект слева от оператора is имеет значение справа от оператора. тогда оператор is возвращает
true, иначе возвращается false
Например, проверка параметра/переменной на значение null:
void PrintUpper(string? text)
{
if (text is null) return;
Console.WriteLine(text.ToUpper());
}
Или, наоборот, с помощью is not можно проверить отсутствие значения:
void PrintUpper(string? text)
{
if (text is not null)
Console.WriteLine(text.ToUpper());
}

175.

void PrintUpper(string? text)
{
if (text is string)
Console.WriteLine(text.ToUpper());
else
Console.WriteLine("NULL");
}
Подобные проверки еще называются null guard или условно говоря
"защита от null".

176.

Оператор ??
Оператор ?? называется оператором null-объединения. Он применяется для установки значений по умолчанию для типов,
которые допускают значение null:
левый_операнд ?? правый_операнд
Оператор ?? возвращает левый операнд, если этот операнд не равен null. Иначе возвращается правый операнд. При этом
левый операнд должен принимать null. Посмотрим на примере:
string? text = null;
string name = text ?? "Tom"; // равно Tom, так как text равен null
Console.WriteLine(name); // Tom
int? id = 200;
int personid = id ?? 1; // равно 200, так как id не равен null
Console.WriteLine(personid); // 200
Но мы не можем написать следующим образом:
int x = 44;
int y = x ?? 100;

177.

Также можно использовать производный оператора ??=
string? text = null;
text ??= "Sam";
// аналогично
// text = text ?? "Sam";
Console.WriteLine(text); // Sam
int? id = 100;
id ??= 1;
// аналогично
//id = id ?? 1;
Console.WriteLine(id); // 100

178.

Псевдонимы
using printer = System.Console;
printer.WriteLine("Laudate omnes gentes laudate");
printer.WriteLine("Magnificat in secula");

179.

using printer = System.Console;
using User = Person;
User tom = new User("Tom");
printer.WriteLine(tom.Name); // Tom
class Person
{
public string Name { get; set; }
public Person(string name) => Name = name;
}

180.

Статический импорт
using static System.Console;
WriteLine("Hello from C# 8.0");

181.

using static System.Console;
using static Operation;
WriteLine(Sum(5, 4));
// 9
WriteLine(Subtract(5, 4)); // 1
WriteLine(Multiply(5, 4)); // 20
static class Operation
{
public static int Sum(int a, int b) => a + b;
public static int Subtract(int a, int b) => a - b;
public static int Multiply(int a, int b) => a * b;
}

182.

Работа с файлами. Классы File и FileInfo
Некоторые полезные методы и свойства класса FileInfo:
CopyTo(path): копирует файл в новое место по указанному
пути path
Create(): создает файл
Delete(): удаляет файл
MoveTo(destFileName): перемещает файл в новое место
Свойство Directory: получает родительский каталог в виде
объекта DirectoryInfo
Свойство DirectoryName: получает полный путь к
родительскому каталогу
Свойство Exists: указывает, существует ли файл
Свойство Length: получает размер файла
Свойство Extension: получает расширение файла
Свойство Name: получает имя файла
Свойство FullName: получает полное имя файла

183.

Класс File реализует похожую функциональность с
помощью статических методов:
• Copy(): копирует файл в новое место
• Create(): создает файл
• Delete(): удаляет файл
• Move: перемещает файл в новое место
• Exists(file): определяет, существует ли файл

184.

Получение информации о файле
string path = @"C:\apache\hta.txt";
FileInfo fileInf = new FileInfo(path);
if (fileInf.Exists)
{
Console.WriteLine("Имя файла: {0}", fileInf.Name);
Console.WriteLine("Время создания: {0}", fileInf.CreationTime);
Console.WriteLine("Размер: {0}", fileInf.Length);
}

185.

Удаление файла
string path = @"C:\apache\hta.txt";
FileInfo fileInf = new FileInfo(path);
if (fileInf.Exists)
{
fileInf.Delete();
// альтернатива с помощью класса File
// File.Delete(path);
}

186.

Перемещение файла
string path = @"C:\apache\hta.txt";
string newPath = @"C:\SomeDir\hta.txt";
FileInfo fileInf = new FileInfo(path);
if (fileInf.Exists)
{
fileInf.MoveTo(newPath);
// альтернатива с помощью класса File
// File.Move(path, newPath);
}

187.

Копирование файла
string path = @"C:\apache\hta.txt";
string newPath = @"C:\SomeDir\hta.txt";
FileInfo fileInf = new FileInfo(path);
if (fileInf.Exists)
{
fileInf.CopyTo(newPath, true);
// альтернатива с помощью класса File
// File.Copy(path, newPath, true);
}

188.

Создание FileStream
Класс FileStream представляет возможности по
считыванию из файла и записи в файл. Он позволяет
работать как с текстовыми файлами, так и с
бинарными.
Для создания объекта FileStream можно использовать
как конструкторы этого класса, так и статические
методы класса File. Конструктор FileStream имеет
множество перегруженных версий, из которых отмечу
лишь одну, самую простую и используемую:
FileStream(string filename, FileMode mode)

189.

Append: если файл существует, то текст добавляется в
конец файл. Если файла нет, то он создается. Файл
открывается только для записи.
Create: создается новый файл. Если такой файл уже
существует, то он перезаписывается
CreateNew: создается новый файл. Если такой файл уже
существует, то он приложение выбрасывает ошибку
Open: открывает файл. Если файл не существует,
выбрасывается исключение
OpenOrCreate: если файл существует, он открывается,
если нет - создается новый
Truncate: если файл существует, то он перезаписывается.
Файл открывается только для записи.

190.

Другой способ создания объекта FileStream представляют
статические методы класса File:
FileStream File.Open(string file, FileMode mode);
FileStream File.OpenRead(string file);
FileStream File.OpenWrite(string file);
Первый метод открывает файл с учетом объекта FileMode и
возвращает файловой поток FileStream. У этого метода также есть
несколько перегруженных версий. Второй метод открывает поток
для чтения, а третий открывает поток для записи.

191.

Свойства и методы FileStream
Рассмотрим наиболее важные его свойства и методы класса FileStream:
Свойство Length: возвращает длину потока в байтах
Свойство Position: возвращает текущую позицию в потоке
void CopyTo(Stream destination): копирует данные из текущего потока в поток
destination
int Read(byte[] array, int offset, int count): считывает данные из файла в массив
байтов и возвращает количество успешно считанных байтов. Принимает три
параметра:
• array - массив байтов, куда будут помещены считываемые из файла данные
• offset представляет смещение в байтах в массиве array, в который считанные
байты будут помещены
• count - максимальное число байтов, предназначенных для чтения. Если в
файле находится меньшее количество байтов, то все они будут считаны.

192.

long Seek(long offset, SeekOrigin origin): устанавливает позицию в
потоке со смещением на количество байт, указанных в параметре
offset.
void Write(byte[] array, int offset, int count): записывает в файл
данные из массива байтов. Принимает три параметра:
• array - массив байтов, откуда данные будут записываться в файл
• offset - смещение в байтах в массиве array, откуда начинается
запись байтов в поток
• count - максимальное число байтов, предназначенных для записи

193.

Произвольный доступ к файлам
С помощью метода Seek() мы можем управлять положением
курсора потока, начиная с которого производится считывание или
запись в файл. Этот метод принимает два параметра: offset
(смещение) и позиция в файле. Позиция в файле описывается
тремя значениями:
• SeekOrigin.Begin: начало файла
• SeekOrigin.End: конец файла
• SeekOrigin.Current: текущая позиция в файле

194.

Запись в файл и StreamWriter
Для записи в текстовый файл используется класс StreamWriter.
Некоторые из его конструкторов, которые могут применяться для
создания объекта StreamWriter:
• StreamWriter(string path): через параметр path передается путь к
файлу, который будет связан с потоком
• StreamWriter(string path, bool append): параметр append указывает,
надо ли добавлять в конец файла данные или же перезаписывать
файл. Если равно true, то новые данные добавляются в конец файла.
Если равно false, то файл перезаписываетсяя заново
• StreamWriter(string path, bool append, System.Text.Encoding encoding):
параметр encoding указывает на кодировку, которая будет
применяться при записи

195.

Свою функциональность StreamWriter реализует через следующие
методы:
• int Close(): закрывает записываемый файл и освобождает все ресурсы
• void Flush(): записывает в файл оставшиеся в буфере данные и
очищает буфер.
• void Write(string value): записывает в файл данные простейших типов,
как int, double, char, string и т.д. Соответственно имеет ряд
перегруженных версий для записи данных элементарных типов,
например, Write(char value), Write(int value), Write(double value) и т.д.
• void WriteLine(string value): также записывает данные, только после
записи добавляет в файл символ окончания строки

196.

using System;
using System.IO;
namespace HelloApp {
class Program {
static void Main(string[] args) {
string writePath = @"C:\SomeDir\hta.txt";
string text = "Привет мир!\nПока мир...";
try {
using (StreamWriter sw = new StreamWriter(writePath, false, System.Text.Encoding.Default))
{
sw.WriteLine(text);
}
using (StreamWriter sw = new StreamWriter(writePath, true, System.Text.Encoding.Default))
{
sw.WriteLine("Дозапись");
sw.Write(4.5);
Привет мир!
}
Пока мир...
Console.WriteLine("Запись выполнена");
}
Дозапись
catch (Exception e) {
4,5
Console.WriteLine(e.Message);
}}}}

197.

Чтение из файла и StreamReader
Класс StreamReader позволяет нам легко считывать весь текст или
отдельные строки из текстового файла.
Некоторые из конструкторов класса StreamReader:
• StreamReader(string path): через параметр path передается путь к
считываемому файлу
• StreamReader(string path, System.Text.Encoding encoding):
параметр encoding задает кодировку для чтения файла

198.

Среди методов StreamReader можно выделить следующие:
• void Close(): закрывает считываемый файл и освобождает все ресурсы
• int Peek(): возвращает следующий доступный символ, если символов больше нет, то
возвращает -1
• int Read(): считывает и возвращает следующий символ в численном представлении. Имеет
перегруженную версию: Read(char[] array, int index, int count), где array - массив, куда
считываются символы, index - индекс в массиве array, начиная с которого записываются
считываемые символы, и count - максимальное количество считываемых символов
• string ReadLine(): считывает одну строку в файле
• string ReadToEnd(): считывает весь текст из файла

199.

using System;
using System.IO;
using System.Threading.Tasks;
namespace HelloApp
{
class Program
{
static async Task Main(string[] args)
{
string path = @"C:\SomeDir\hta.txt";
try
{
using (StreamReader sr = new StreamReader(path))
{
Console.WriteLine(sr.ReadToEnd());
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}

200.

string path= @"C:\SomeDir\hta.txt";
using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Default))
{
string line;
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}

201.

Наследование
Наследование (inheritance) является одним из ключевых моментов ООП. Благодаря наследованию один
класс может унаследовать функциональность другого класса.
Пусть у нас есть следующий класс Person, который описывает отдельного человека:
class Person
{
private string _name = "";
public string Name
{
get { return _name; }
set { _name = value; }
}
public void Print()
{
Console.WriteLine(Name);
}
}
class Employee : Person
{
}

202.

Таким образом, наследование реализует отношение is-a (является),
объект класса Employee также является объектом класса Person:
Person person = new Person { Name = "Tom" };
person.Print(); // Tom
person = new Employee { Name = "Sam" };
person.Print(); // Sam
И поскольку объект Employee является также и объектом Person, то
мы можем так определить переменную: Person p = new Employee().

203.

По умолчанию все классы наследуются от базового класса Object, даже если мы явным образом не
устанавливаем наследование. Поэтому выше определенные классы Person и Employee кроме своих
собственных методов, также будут иметь и методы класса Object: ToString(), Equals(), GetHashCode() и
GetType().
Все классы по умолчанию могут наследоваться. Однако здесь есть ряд ограничений:
• Не поддерживается множественное наследование, класс может наследоваться только от одного
класса.
• При создании производного класса надо учитывать тип доступа к базовому классу - тип доступа к
производному классу должен быть таким же, как и у базового класса, или более строгим. То есть, если
базовый класс у нас имеет тип доступа internal, то производный класс может иметь тип доступа
internal или private, но не public.
• Однако следует также учитывать, что если базовый и производный класс находятся в разных сборках
(проектах), то в этом случае производый класс может наследовать только от класса, который имеет
модификатор public.
• Если класс объявлен с модификатором sealed, то от этого класса нельзя наследовать и создавать
производные классы. Например, следующий класс не допускает создание наследников:
sealed class Admin
{
}
• Нельзя унаследовать класс от статического класса.

204.

Доступ к членам базового класса из
класса-наследника
class Employee : Person
{
public void PrintName()
{
Console.WriteLine(_name);
}
}
class Employee : Person
{
public void PrintName()
{
Console.WriteLine(Name);
}
}

205.

Ключевое слово base
class Person
{
public string Name { get; set;}
public Person(string name)
{
Name = name;
}
public void Print()
{
Console.WriteLine(Name);
}
}
class Employee : Person
{
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
{
Company = company;
}
}
Person person = new Person("Bob");
person.Print(); // Bob
Employee employee = new Employee("Tom", "Microsoft");
employee.Print(); // Tom

206.

Конструкторы в производных классах
class Employee : Person
{
public string Company { get; set; } = "";
}
class Employee : Person
{
public string Company { get; set; } = "";
public Employee(string name, string company) // ! Ошибка
{
Name = name;
Company = company;
}
}

207.

class Employee : Person
{
public string Company { get; set; } = "";
public Employee(string name, string company)
: base(name)
{
Company = company;
}
}

208.

class Person
{
public string Name { get; set; }
// конструктор без параметров
public Person()
{
Name = "Tom";
Console.WriteLine("Вызов конструктора без параметров");
}
public Person(string name)
{
Name = name;
}
public void Print()
{
Console.WriteLine(Name);
}
}

209.

Преобразование типов
class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
public void Print()
{
Console.WriteLine($"Person {Name}");
}
}
class Employee : Person
{
public string Company { get; set; }
public Employee(string name, string company) : base(name)
{
Company = company;
}
}
class Client : Person
{
public string Bank { get; set; }
public Client(string name, string bank) : base(name)
{
Bank = bank;
}
}

210.

211.

Восходящие преобразования. Upcasting
Employee employee = new Employee("Tom", "Microsoft");
Person person = employee; // преобразование от Employee к Person
Console.WriteLine(person.Name);

212.

Person bob = new Client("Bob", "ContosoBank"); // преобразование от Client к Person
object person1 = new Employee("Tom", "Microsoft"); // от Employee к object
object person2 = new Client("Bob", "ContosoBank"); // от Client к object
object person3 = new Person("Sam");
// от Person к object

213.

Нисходящие преобразования. Downcasting
Employee employee = new Employee("Tom", "Microsoft");
Person person = employee; // преобразование от Employee к Person
__________________________________________________________
Employee employee1 = new Employee("Tom", "Microsoft");
Person person = employee1; // преобразование от Employee к
Person
//Employee employee2 = person; // так нельзя, нужно явное
преобразование
Employee employee2 = (Employee)person; // преобразование от
Person к Employee

214.

// Объект Employee также представляет тип object
object obj = new Employee("Bill", "Microsoft");
// чтобы обратиться к возможностям типа Employee,
//приводим объект к типу Employee
Employee employee = (Employee) obj;
// объект Client также представляет тип Person
Person person = new Client("Sam", "ContosoBank");
// преобразование от типа Person к Client
Client client = (Client)person;

215.

// Объект Employee также представляет тип object
object obj = new Employee("Bill", "Microsoft");
// преобразование к типу Person для вызова метода Print
((Person)obj).Print();
// либо так
// ((Employee)obj).Print();
// преобразование к типу Employee, чтобы получить свойство Company
string company = ((Employee)obj).Company;

216.

// Объект Employee также представляет тип object
object obj = new Employee("Bill", "Microsoft");
// преобразование к типу Client, чтобы получить свойство Bank
string bank = ((Client)obj).Bank;

217.

Employee employee1 = new Person("Tom"); // ! Ошибка
Person person = new Person("Bob");
Employee employee2 = (Employee) person; // ! Ошибка

218.

Способы преобразований
Во-первых, можно использовать ключевое слово as. С помощью него программа
пытается преобразовать выражение к определенному типу, при этом не выбрасывает
исключение. В случае неудачного преобразования выражение будет содержать
значение null:
Person person = new Person("Tom");
Employee? employee = person as Employee;
if (employee == null)
{
Console.WriteLine("Преобразование прошло неудачно");
}
else
{
Console.WriteLine(employee.Company);
}

219.

Второй способ заключается в проверке допустимости преобразования с помощью ключевого
слова is:
значение is тип
Если значение слева от оператора представляет тип, указанный справа от оператора, то
оператор is возвращает true, иначе возвращается false.
Причем оператор is позволяет автоматически преобразовать значение к типу, если это
значение представляет данный тип. Например:
Person person = new Person("Tom");
if (person is Employee employee)
{
Console.WriteLine(employee.Company);
}
else
{
Console.WriteLine("Преобразование не допустимо");
}

220.

Оператор is также можно применять и без преобразования, просто
проверяя на соответствие типу:
Person person = new Person("Tom");
if (person is Employee)
{
Console.WriteLine("Представляет тип Employee");
}
else
{
Console.WriteLine("НЕ является объектом типа Employee");
}

221.

Виртуальные методы и свойства
class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
Person bob = new Person("Bob");
}
bob.Print(); // вызов метода Print из класса Person
public virtual void Print()
{
Employee tom = new Employee("Tom", "Microsoft");
Console.WriteLine(Name);
tom.Print(); // вызов метода Print из класса Person
}
}
class Employee : Person
{
public string Company { get; set; }
Консольный вывод:
public Employee(string name, string company) : base(name)
{
Bob
Company = company;
Tom
}
}

222.

class Employee : Person
{
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
Person bob = new Person("Bob");
{
bob.Print(); // вызов метода Print из класса Person
Company = company;
Employee tom = new Employee("Tom", "Microsoft");
}
tom.Print(); // вызов метода Print из класса Employee
public override void Print()
{
Console.WriteLine($"{Name} работает в {Company}");
}
Консольный вывод:
}
Bob
Tom работает в Microsoft

223.

При переопределении виртуальных методов следует
учитывать ряд ограничений:
• Виртуальный и переопределенный методы должны
иметь один и тот же модификатор доступа. То есть
если виртуальный метод определен с помощью
модификатора public, то и переопредленный метод
также должен иметь модификатор public.
• Нельзя переопределить или объявить виртуальным
статический метод.

224.

Ключевое слово base
Кроме конструкторов, мы можем обратиться с помощью ключевого слова base к другим членам
базового класса. В нашем случае вызов base.Print(); будет обращением к методу Print() в классе Person:
class Employee : Person
{
public string Company { get; set; }
public Employee(string name, string company)
:base(name)
{
Company = company;
}
public override void Print()
{
base.Print();
Console.WriteLine($"работает в {Company}");
}
}

225.

Переопределение свойств
class Person
{
int age = 1;
public virtual int Age
{
get => age;
set{ if(value > 0 && value < 110) age = value; }
}
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
public virtual void Print() => Console.WriteLine(Name);
}
class Employee : Person
{
public override int Age
{
get => base.Age;
set { if (value > 17 && value < 110) base.Age = value; }
}
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
{
Company = company;
base.Age = 18; // возраст для работников по умолчанию
}
}

226.

Person bob = new Person("Bob");
Console.WriteLine(bob.Age); // 1
Employee tom = new Employee("Tom", "Microsoft");
Console.WriteLine(tom.Age); // 18
tom.Age = 22;
Console.WriteLine(tom.Age); // 22
tom.Age = 12;
Console.WriteLine(tom.Age); // 22

227.

Запрет переопределения методов
Также можно запретить переопределение методов и свойств. В этом случае их надо объявлять с
модификатором sealed:
class Employee : Person
{
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
{
Company = company;
}
public override sealed void Print()
{
Console.WriteLine($"{Name} работает в {Company}");
}
}

228.

Скрытие методов и свойств
class Person {
public string Name { get; set; }
public Person(string name) {
Name = name;
}
public void Print() {
Console.WriteLine($"Name: {Name}");
}
Используем эти классы в программе в методе Main:
Person bob = new Person("Bob");
bob.Print(); // Name: Bob
}
class Employee : Person {
public string Company { get; set; }
Employee tom = new Employee("Tom", "Microsoft");
tom.Print(); // Name: Tom Company: Microsoft
public Employee(string name, string company)
: base(name) {
Company = company;
}
public new void Print() {
Console.WriteLine($"Name: {Name} Company: {Company}");
}
}

229.

class Employee : Person
{
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
{
Company = company;
}
public new void Print()
{
base.Print(); // вызываем метод Print из базового класса Person
Console.WriteLine($"Company: {Company}");
}
}

230.

Скрытие свойств
Person bob = new Person("Bob");
Console.WriteLine(bob.Name); // Bob
Employee tom = new Employee("Tom", "Microsoft");
Console.WriteLine(tom.Name); // Mr./Ms. Tom
class Person {
public string Name { get; set; }
public Person(string name) {
Name = name;
} }
class Employee : Person {
public new string Name { // скрываем свойство Name базового класса
get => $"Mr./Ms. {base.Name}";
set => base.Name = value;
}
public string Company { get; set; }
public Employee(string name, string company)
: base(name) {
Company = company;
} }

231.

Скрытие переменных и констант
Console.WriteLine(Person.minAge); // 1
Console.WriteLine(Person.typeName); // Person
Console.WriteLine(Employee.minAge); // 18
Console.WriteLine(Employee.typeName); // Employee
class Person {
public readonly static int minAge = 1;
public const string typeName = "Person";
}
class Employee : Person {
// скрываем поля и константы базового класса
public new readonly static int minAge = 18;
public new const string typeName = "Employee";
}

232.

Различие переопределения и
скрытия методов
class Person {
public string Name { get; set; }
public Person(string name)
{
Используем классы в программе:
Name = name;
}
Person tom = new Employee("Tom", "Microsoft");
public virtual void Print()
tom.Print();
// Tom работает в Microsoft
{
Console.WriteLine(Name);
}
}
class Employee : Person {
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
{
Company = company;
}
public override void Print()
{
Console.WriteLine($"{Name} работает в {Company}");
}
}

233.

class Person {
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
И посмотрим, что будет в следующем случае:
public void Print()
{
Person tom = new Employee("Tom", "Microsoft");
Console.WriteLine(Name);
tom.Print();
// Tom
}
}
class Employee : Person {
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
{
Company = company;
}
public new void Print()
{
Console.WriteLine($"{Name} работает в {Company}");
}
}

234.

Абстрактные классы и члены
классов
abstract class Transport
{
public void Move()
{
Console.WriteLine("Транспортно средство движется");
}
}
Transport tesla = new Transport();

235.

Transport car = new Car();
Transport ship = new Ship();
Transport aircraft = new Aircraft();
car.Move();
ship.Move();
aircraft.Move();
abstract class Transport {
public void Move()
{
Console.WriteLine("Транспортное средство движется");
}
}
class Ship : Transport { } // класс корабля
class Aircraft : Transport { } // класс самолета
class Car : Transport { } // класс машины

236.

Transport car = new Car("машина");
Transport ship = new Ship("корабль");
Transport aircraft = new Aircraft("самолет");
car.Move();
// машина движется
ship.Move();
// корабль движется
aircraft.Move(); // самолет движется
abstract class Transport {
public string Name { get; }
// конструктор абстрактного класса Transport
public Transport(string name)
{
Name = name;
}
public void Move() =>Console.WriteLine($"{Name} движется");
}
class Ship : Transport { // класс корабля
public Ship(string name) : base(name) { } // вызываем конструктор базового класса
}
class Aircraft : Transport { // класс самолета
public Aircraft(string name) : base(name) { }
}
class Car : Transport { // класс машины
public Car(string name) : base(name) { }
}

237.

Класс System.Object и его методы
ToString
int i = 5;
Console.WriteLine(i.ToString()); // выведет число 5
double d = 3.5;
Console.WriteLine(d.ToString()); // выведет число 3,5

238.

Person person = new Person { Name = "Tom" };
Console.WriteLine(person.ToString()); // выведет название класса Person
Clock clock = new Clock { Hours = 15, Minutes = 34, Seconds = 53 };
Console.WriteLine(clock.ToString()); // выведет 15:34:53
class Clock {
public int Hours { get; set; }
public int Minutes { get; set; }
public int Seconds { get; set; }
public override string ToString()
{
return $"{Hours}:{Minutes}:{Seconds}";
}
}
class Person {
public string Name { get; set; } = "";
}

239.

Метод GetHashCode
class Person
{
public string Name { get; set; } = "";
public override int GetHashCode()
{
return Name.GetHashCode();
}
}

240.

Получение типа объекта и метод GetType
Метод GetType позволяет получить тип данного объекта:
Person person = new Person { Name = "Tom" };
Console.WriteLine(person.GetType()); // Person
Этот метод возвращает объект Type, то есть тип объекта.
С помощью ключевого слова typeof мы получаем тип класса и сравниваем его
с типом объекта. И если этот объект представляет тип Client, то выполняем
определенные действия.
object person = new Person { Name = "Tom" };
if (person.GetType() == typeof(Person))
Console.WriteLine("Это реально класс Person");

241.

Метод Equals
Метод Equals позволяет сравнить два объекта на равенство. В качестве параметра он принимает
объект для сравнения в виде типа object и возврашает true, если оба объекта равны:
public override bool Equals(object? obj) {......}
Например, реализуем данный метод в классе Person:
class Person {
public string Name { get; set; } = "";
public override bool Equals(object? obj) {
// если параметр метода представляет тип Person
// то возвращаем true, если имена совпадают
if (obj is Person person) return Name == person.Name;
return false;
}
// вместе с методом Equals следует реализовать метод GetHashCode
public override int GetHashCode() => Name.GetHashCode();
}

242.

Применение метода:
var person1 = new Person { Name = "Tom" };
var person2 = new Person { Name = "Bob" };
var person3 = new Person { Name = "Tom" };
bool person1EqualsPerson2 = person1.Equals(person2); // false
bool person1EqualsPerson3 = person1.Equals(person3); // true
Console.WriteLine(person1EqualsPerson2); // false
Console.WriteLine(person1EqualsPerson3); // true

243.

Обработка исключений
Конструкция try..catch..finally
try
{
}
catch
{
}
finally
{
}

244.

Рассмотрим следующий пример:
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат: {y}");
Console.WriteLine("Конец программы");

245.

try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат: {y}");
}
catch
{
Console.WriteLine("Возникло исключение!");
}
finally
{
Console.WriteLine("Блок finally");
}
Console.WriteLine("Конец программы");
Возникло исключение!
Блок finally
Конец программы

246.

try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат:
{y}");
}
catch
{
Console.WriteLine("Возникло
исключение!");
}
try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат:
{y}");
}
finally
{
Console.WriteLine("Блок finally");
}

247.

Обработка исключений и условные
конструкции
Square("12"); // Квадрат числа 12: 144
Square("ab"); // !Исключение
void Square(string data)
{
int x = int.Parse(data);
Console.WriteLine($"Квадрат числа {x}: {x * x}");
}

248.

Square("12"); // Квадрат числа 12: 144
Square("ab"); // Некорректный ввод
void Square(string data)
{
if (int.TryParse(data, out var x))
{
Console.WriteLine($"Квадрат числа {x}: {x * x}");
}
else
{
Console.WriteLine("Некорректный ввод");
}
}

249.

Определение блока catch
За обработку исключения отвечает блок catch
catch
{
// выполняемые инструкции
}
catch (тип_исключения)
{
// выполняемые инструкции
}

250.

try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат: {y}");
}
catch(DivideByZeroException)
{
Console.WriteLine("Возникло исключение DivideByZeroException");
}

251.

catch (тип_исключения имя_переменной)
{
// выполняемые инструкции
}
try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат: {y}");
}
catch(DivideByZeroException ex)
{
Console.WriteLine($"Возникло исключение {ex.Message}");
}

252.

Фильтры исключений
catch when(условие)
{
}

253.

int x = 1;
int y = 0;
try
{
int result1 = x / y;
int result2 = y / x;
}
catch (DivideByZeroException) when (y == 0)
{
Console.WriteLine("y не должен быть равен 0");
}
catch(DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}

254.

int x = 0;
int y = 1;
try
{
int result1 = x / y;
int result2 = y / x;
}
catch (DivideByZeroException) when (y == 0)
{
Console.WriteLine("y не должен быть равен 0");
}
catch(DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}

255.

Типы исключений. Класс Exception
Базовым для всех типов исключений является тип Exception.
Этот тип определяет ряд свойств, с помощью которых можно
получить информацию об исключении.
• InnerException: хранит информацию об исключении,
которое послужило причиной текущего исключения
• Message: хранит сообщение об исключении, текст ошибки
• Source: хранит имя объекта или сборки, которое вызвало
исключение
• StackTrace: возвращает строковое представление стека
вызывов, которые привели к возникновению исключения
• TargetSite: возвращает метод, в котором и было вызвано
исключение

256.

try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат: {y}");
}
catch (Exception ex)
{
Console.WriteLine($"Исключение: {ex.Message}");
Console.WriteLine($"Метод: {ex.TargetSite}");
Console.WriteLine($"Трассировка стека: {ex.StackTrace}");
}

257.

• DivideByZeroException: представляет исключение,
которое генерируется при делении на ноль
• ArgumentOutOfRangeException: генерируется, если
значение аргумента находится вне диапазона
допустимых значений
• ArgumentException: генерируется, если в метод для
параметра передается некорректное значение
• IndexOutOfRangeException: генерируется, если индекс
элемента массива или коллекции находится вне
диапазона допустимых значений
• InvalidCastException: генерируется при попытке
произвести недопустимые преобразования типов
• NullReferenceException: генерируется при попытке
обращения к объекту, который равен null (то есть по сути
неопределен)

258.

static void Main(string[] args)
{
try {
int[] numbers = new int[4];
numbers[7] = 9; // IndexOutOfRangeException
int x = 5;
int y = x / 0; // DivideByZeroException
Console.WriteLine($"Результат: {y}");
}
catch (DivideByZeroException) {
Console.WriteLine("Возникло исключение DivideByZeroException");
}
catch (IndexOutOfRangeException ex) {
Console.WriteLine(ex.Message);
}
Console.Read();
}

259.

try
{
object obj = "you";
int num = (int)obj; // System.InvalidCastException
Console.WriteLine($"Результат: {num}");
}
catch (DivideByZeroException)
{
Console.WriteLine("Возникло исключение DivideByZeroException");
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Возникло исключение IndexOutOfRangeException");
}

260.

Генерация исключения и оператор throw
try {
Console.Write("Введите имя: ");
string? name = Console.ReadLine();
if (name== null || name.Length < 2)
{
throw new Exception("Длина имени меньше 2 символов");
}
else
{
Console.WriteLine($"Ваше имя: {name}");
}
}
catch (Exception e) {
Console.WriteLine($"Ошибка: {e.Message}");
}

261.

try {
try {
Console.Write("Введите имя: ");
string? name = Console.ReadLine();
if (name == null || name.Length < 2) {
throw new Exception("Длина имени меньше 2 символов");
}
else {
Console.WriteLine($"Ваше имя: {name}");
}
}
catch (Exception e) {
Console.WriteLine($"Ошибка: {e.Message}");
throw;
}
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}

262.

Создание классов исключений
try {
Person person = new Person { Name = "Tom", Age = 17 };
}
catch (Exception ex) {
Console.WriteLine($"Ошибка: {ex.Message}");
}
class Person {
private int age;
public string Name { get; set; } = "";
public int Age {
get => age;
set {
if (value < 18)
throw new Exception("Лицам до 18 регистрация запрещена");
else
age = value;
} } }

263.

class PersonException : Exception {
public PersonException(string message)
: base(message) { }
}
_________________________________________________________________________
try {
Person person = new Person { Name = "Tom", Age = 17 };
}
catch (PersonException ex)\ {
Console.WriteLine($"Ошибка: {ex.Message}");
}
class Person {
private int age;
public string Name { get; set; } = "";
public int Age {
get => age;
set {
if (value < 18)
throw new PersonException("Лицам до 18 регистрация запрещена");
else
age = value;
} } }

264.

class PersonException :
ArgumentException{
public int Value { get;}
public PersonException(string
message, int val)
: base(message) {
Value = val;
}
}
try {
Person person = new Person { Name = "Tom", Age = 17
};
}
catch (PersonException ex) {
Console.WriteLine($"Ошибка: {ex.Message}");
Console.WriteLine($"Некорректное значение:
{ex.Value}");
}
class Person {
private int age;
public string Name { get; set; } = "";
public int Age {
get => age;
set {
if (value < 18)
throw new PersonException("Лицам до 18
регистрация запрещена", value);
else
age = value;
} } }

265.

Делегаты
Для объявления делегата используется ключевое
слово delegate, после которого идет возвращаемый
тип, название и параметры.
Например:
delegate void Message();

266.

Message mes;
mes = Hello;
mes();
// 2. Создаем переменную
делегата
// 3. Присваиваем этой переменной
адрес метода
// 4. Вызываем метод
void Hello() => Console.WriteLine("Hello METANIT.COM");
delegate void Message(); // 1. Объявляем делегат

267.

Message message1 = Welcome.Print;
Message message2 = new Hello().Display;
message1(); // Welcome
message2(); // Привет
delegate void Message();
class Welcome
{
public static void Print() => Console.WriteLine("Welcome");
}
class Hello
{
public void Display() => Console.WriteLine("Привет");
}

268.

Место определения делегата
class Program
{
delegate void Message(); // 1. Объявляем делегат
static void Main()
{
Message mes;
// 2. Создаем переменную делегата
mes = Hello;
// 3. Присваиваем этой переменной адрес метода
mes();
// 4. Вызываем метод
void Hello() => Console.WriteLine("Hello METANIT.COM");
}
}

269.

delegate void Message(); // 1. Объявляем делегат
class Program
{
static void Main()
{
Message mes;
// 2. Создаем переменную делегата
mes = Hello;
// 3. Присваиваем этой переменной адрес
метода
mes();
// 4. Вызываем метод
void Hello() => Console.WriteLine("Hello METANIT.COM");
}
}

270.

Параметры и результат делегата
Operation operation = Add; // делегат указывает на метод Add
int result = operation(4, 5); // фактически Add(4, 5)
Console.WriteLine(result); // 9
operation = Multiply;
// теперь делегат указывает на метод Multiply
result = operation(4, 5);
// фактически Multiply(4, 5)
Console.WriteLine(result); // 20
int Add(int x, int y) => x + y;
int Multiply(int x, int y) => x * y;
delegate int Operation(int x, int y);

271.

Присвоение ссылки на метод
Operation operation1 = Add;
Operation operation2 = new Operation(Add);
int Add(int x, int y) => x + y;
delegate int Operation(int x, int y);

272.

Соответствие методов делегату
delegate void SomeDel(int a, double b);
Этому делегату соответствует, например, следующий метод:
void SomeMethod1(int g, double n) { }
А следующие методы НЕ соответствуют:
double SomeMethod2(int g, double n) { return g + n; }
void SomeMethod3(double n, int g) { }
void SomeMethod4(ref int g, double n) { }
void SomeMethod5(out int g, double n) { g = 6; }

273.

Добавление методов в делегат
Message message = Hello;
message += HowAreYou; // теперь message указывает на два
метода
message();
// вызываются оба метода - Hello и
HowAreYou
void Hello() => Console.WriteLine("Hello");
void HowAreYou() => Console.WriteLine("How are you?");
delegate void Message();

274.

Message message = Hello;
message += HowAreYou;
message += Hello;
message += Hello;
message();
Консольный вывод:
Hello
How are you?
Hello
Hello

275.

Message? message = Hello;
message += HowAreYou;
message(); // вызываются все методы из message
message -= HowAreYou; // удаляем метод HowAreYou
if (message != null) message(); // вызывается метод Hello

276.

Объединение делегатов
Message mes1 = Hello;
Message mes2 = HowAreYou;
Message mes3 = mes1 + mes2; // объединяем делегаты
mes3(); // вызываются все методы из mes1 и mes2
void Hello() => Console.WriteLine("Hello");
void HowAreYou() => Console.WriteLine("How are you?");
delegate void Message();

277.

Вызов делегата
Message mes = Hello;
mes();
Operation op = Add;
int n = op(3, 4);
Console.WriteLine(n);
void Hello() => Console.WriteLine("Hello");
int Add(int x, int y) => x + y;
delegate int Operation(int x, int y);
delegate void Message();

278.

Другой способ вызова делегата представляет метод Invoke():
Message mes = Hello;
mes.Invoke(); // Hello
Operation op = Add;
int n = op.Invoke(3, 4);
Console.WriteLine(n); // 7
void Hello() => Console.WriteLine("Hello");
int Add(int x, int y) => x + y;
delegate int Operation(int x, int y);
delegate void Message();

279.

Message? mes;
//mes();
// ! Ошибка: делегат равен null
Operation? op = Add;
op -= Add; // делегат op пуст
int n = op(3, 4);
// !Ошибка: делегат равен null
_______________________________________________
Message? mes = null;
mes?.Invoke();
// ошибки нет, делегат просто не вызывается
Operation? op = Add;
op -= Add;
// делегат op пуст
int? n = op?.Invoke(3, 4); // ошибки нет, делегат просто не вызывается, а
n = null

280.

Operation op = Subtract;
op += Multiply;
op += Add;
Console.WriteLine(op(7, 2)); // Add(7,2) = 9
int Add(int x, int y) => x + y;
int Subtract(int x, int y) => x - y;
int Multiply(int x, int y) => x * y;
delegate int Operation(int x, int y);

281.

Обобщенные делегаты
Operation<decimal, int> squareOperation = Square;
decimal result1 = squareOperation(5);
Console.WriteLine(result1); // 25
Operation<int, int> doubleOperation = Double;
int result2 = doubleOperation(5);
Console.WriteLine(result2); // 10
decimal Square(int n) => n * n;
int Double(int n) => n + n;
delegate T Operation<T, K>(K val);

282.

Делегаты как параметры методов
DoOperation(5, 4, Add);
// 9
DoOperation(5, 4, Subtract); // 1
DoOperation(5, 4, Multiply); // 20
void DoOperation(int a, int b, Operation op)
{
Console.WriteLine(op(a,b));
}
int Add(int x, int y) => x + y;
int Subtract(int x, int y) => x - y;
int Multiply(int x, int y) => x * y;
delegate int Operation(int x, int y);

283.

Возвращение делегатов из метода
Operation operation = SelectOperation(OperationType.Add);
Console.WriteLine(operation(10, 4)); // 14
operation = SelectOperation(OperationType.Subtract);
Console.WriteLine(operation(10, 4)); // 6
operation = SelectOperation(OperationType.Multiply);
Console.WriteLine(operation(10, 4)); // 40
Operation SelectOperation(OperationType opType) {
switch (opType) {
case OperationType.Add: return Add;
case OperationType.Subtract: return Subtract;
default: return Multiply;
}
}
int Add(int x, int y) => x + y;
int Subtract(int x, int y) => x - y;
int Multiply(int x, int y) => x * y;
enum OperationType {
Add, Subtract, Multiply
}
delegate int Operation(int x, int y);

284.

Применение делегатов
public class Account
{
int sum; // Переменная для хранения суммы
// через конструктор устанавливается начальная сумма на счете
public Account(int sum) => this.sum = sum;
// добавить средства на счет
public void Add(int sum) => this.sum += sum;
// взять деньги с счета
public void Take(int sum)
{
// берем деньги, если на счете достаточно средств
if (this.sum >=sum) this.sum -= sum;
}
}

285.

public class Account
{
int sum;
public Account(int sum) => this.sum = sum;
public void Add(int sum) => this.sum += sum;
public void Take(int sum)
{
if (this.sum >= sum)
{
this.sum -= sum;
Console.WriteLine($"Со счета списано {sum} у.е.");
}
}
}

286.

public delegate void AccountHandler(string message); // Объявляем делегат
public class Account {
int sum;
AccountHandler? taken; // Создаем переменную делегата
public Account(int sum) => this.sum = sum;
public void RegisterHandler(AccountHandler del) {// Регистрируем делегат
taken = del;
}
public void Add(int sum) => this.sum += sum;
public void Take(int sum) {
if (this.sum >= sum) {
this.sum -= sum;
// вызываем делегат, передавая ему сообщение
taken?.Invoke($"Со счета списано {sum} у.е.");
}
else {
taken?.Invoke($"Недостаточно средств. Баланс: {this.sum} у.е.");
} } }

287.

public void Take(int sum)
{
if (this.sum >= sum)
{
this.sum -= sum;
// вызываем делегат, передавая ему сообщение
taken?.Invoke($"Со счета списано {sum} у.е.");
}
else
{
taken?.Invoke($"Недостаточно средств. Баланс: {this.sum} у.е.");
}
}

288.

// создаем банковский счет
Account account = new Account(200);
// Добавляем в делегат ссылку на метод PrintSimpleMessage
account.RegisterHandler(PrintSimpleMessage);
// Два раза подряд пытаемся снять деньги
account.Take(100);
account.Take(150);
void PrintSimpleMessage(string message) => Console.WriteLine(message);
Со счета списано 100 у.е.
Недостаточно средств. Баланс: 100 у.е.

289.

Добавление и удаление методов в
делегате
public delegate void AccountHandler(string message);
public class Account {
int sum;
AccountHandler? taken;
public Account(int sum) => this.sum = sum;
public void RegisterHandler(AccountHandler del) { // Регистрируем делегат
taken += del;
}
public void UnregisterHandler(AccountHandler del) { // Отмена регистрации делегата
taken -= del; // удаляем делегат
}
public void Add(int sum) => this.sum += sum;
public void Take(int sum) {
if (this.sum >= sum)
{
this.sum -= sum;
taken?.Invoke($"Со счета списано {sum} у.е.");
}
else
taken?.Invoke($"Недостаточно средств. Баланс: {this.sum} у.е.");
}
}

290.

Account account = new Account(200);
// Добавляем в делегат ссылку на методы
account.RegisterHandler(PrintSimpleMessage);
Со счета списано 100 у.е.
account.RegisterHandler(PrintColorMessage);
Со счета списано 100 у.е.
// Два раза подряд пытаемся снять деньги
Недостаточно средств. Баланс: 100 у.е.
Недостаточно средств. Баланс: 100 у.е.
account.Take(100);
Со счета списано 50 у.е.
account.Take(150);
// Удаляем делегат
account.UnregisterHandler(PrintColorMessage);
account.Take(50); // снова пытаемся снять деньги
void PrintSimpleMessage(string message) => Console.WriteLine(message);
void PrintColorMessage(string message) {
// Устанавливаем красный цвет символов
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ResetColor(); // Сбрасываем настройки цвета
}

291.

Анонимные методы
С делегатами тесно связаны анонимные методы. Анонимные
методы используются для создания экземпляров делегатов.
Определение анонимных методов начинается с ключевого слова
delegate, после которого идет в скобках список параметров и тело
метода в фигурных скобках:
delegate(параметры)
{
// инструкции
}

292.

MessageHandler handler = delegate (string mes)
{
Console.WriteLine(mes);
};
handler("hello world!");
delegate void MessageHandler(string message);

293.

ShowMessage("hello!", delegate (string mes)
{
Console.WriteLine(mes);
});
static void ShowMessage(string message, MessageHandler handler)
{
handler(message);
}
delegate void MessageHandler(string message);

294.

MessageHandler handler = delegate
{
Console.WriteLine("анонимный метод");
};
handler("hello world!"); // анонимный метод
delegate void MessageHandler(string message);

295.

Operation operation = delegate (int x, int y)
{
return x + y;
};
int result = operation(4, 5);
Console.WriteLine(result);
// 9
delegate int Operation(int x, int y);
______________________________________________
int z = 8;
Operation operation = delegate (int x, int y)
{
return x + y + z;
};
int result = operation(4, 5);
Console.WriteLine(result);
// 17
delegate int Operation(int x, int y);

296.

Лямбды
Лямбда-выражения представляют упрощенную запись
анонимных методов. Лямбда-выражения позволяют создать
емкие лаконичные методы, которые могут возвращать
некоторое значение и которые можно передать в качестве
параметров в другие методы.
Ламбда-выражения имеют следующий синтаксис: слева от
лямбда-оператора => определяется список параметров, а
справа блок выражений, использующий эти параметры:
(список_параметров) => выражение

297.

Message hello = () => Console.WriteLine("Hello");
hello();
// Hello
hello();
// Hello
hello();
// Hello
delegate void Message();

298.

Message hello = () =>
{
Console.Write("Hello ");
Console.WriteLine("World");
};
hello();
// Hello World
_________________________________________
var hello = () => Console.WriteLine("Hello");
hello();
// Hello
hello();
// Hello
hello();
// Hello

299.

Параметры лямбды
Operation sum = (x, y) => Console.WriteLine($"{x} + {y} = {x + y}");
sum(1, 2);
// 1 + 2 = 3
sum(22, 14); // 22 + 14 = 36
delegate void Operation(int x, int y);
_______________________________________________________
var sum = (x, y) => Console.WriteLine($"{x} + {y} = {x + y}"); // ! Ошибка

300.

var sum = (int x, int y) => Console.WriteLine($"{x} + {y} = {x + y}");
sum(1, 2);
// 1 + 2 = 3
sum(22, 14); // 22 + 14 = 36
________________________________________________________
PrintHandler print = message => Console.WriteLine(message);
print("Hello");
// Hello
print("Welcome");
// Welcome
delegate void PrintHandler(string message);

301.

Возвращение результата
var sum = (int x, int y) => x + y;
int sumResult = sum(4, 5);
Console.WriteLine(sumResult);
Operation multiply = (x, y) => x * y;
int multiplyResult = multiply(4, 5);
Console.WriteLine(multiplyResult);
delegate int Operation(int x, int y);
// 9
// 9
// 20
// 20

302.

var subtract = (int x, int y) =>
{
if (x > y) return x - y;
else return y - x;
};
int result1 = subtract(10, 6); // 4
Console.WriteLine(result1); // 4
int result2 = subtract(-10, 6); // 16
Console.WriteLine(result2); // 16

303.

Добавление и удаление действий в
лямбда-выражении
var hello = () => Console.WriteLine("METANIT.COM");
var message = () => Console.Write("Hello ");
message += () => Console.WriteLine("World"); // добавляем анонимное лямбдавыражение
message += hello; // добавляем лямбда-выражение из переменной hello
Hello World
message += Print; // добавляем метод
METANIT.COM
message();
Welcome to C#
Console.WriteLine("--------------"); // для разделения вывода
-------------message -= Print; // удаляем метод
Hello World
message -= hello; // удаляем лямбда-выражение из переменной hello
message?.Invoke(); // на случай, если в message больше нет действий
void Print() => Console.WriteLine("Welcome to C#");

304.

Лямбда-выражение как аргумент метода
int[] integers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int result1 = Sum(integers, x => x > 5); // найдем сумму чисел больше 5
Console.WriteLine(result1); // 30
int result2 = Sum(integers, x => x % 2 == 0); // найдем сумму четных чисел
Console.WriteLine(result2); //20
int Sum(int[] numbers, IsEqual func) {
int result = 0;
foreach (int i in numbers) {
if (func(i))
result += i;
}
return result;
}
delegate bool IsEqual(int x);

305.

Лямбда-выражение как результат метода
Operation operation = SelectOperation(OperationType.Add);
Console.WriteLine(operation(10, 4)); // 14
operation = SelectOperation(OperationType.Subtract);
Console.WriteLine(operation(10, 4)); // 6
operation = SelectOperation(OperationType.Multiply);
Console.WriteLine(operation(10, 4)); // 40
Operation SelectOperation(OperationType opType) {
switch (opType) {
case OperationType.Add: return (x, y) => x + y;
case OperationType.Subtract: return (x, y) => x - y;
default: return (x, y) => x * y;
} }
enum OperationType {
Add, Subtract, Multiply
}
delegate int Operation(int x, int y);

306.

События
class Account
{
public int Sum { get; private set; } // сумма на счете
public Account(int sum) => Sum = sum; // в конструкторе устанавливаем
начальную сумму на счете
public void Put(int sum) => Sum += sum; // добавление средств на счет
public void Take(int sum) // списание средств со счета
{
if (Sum >= sum)
{
Sum -= sum;
}
}
}

307.

Account account = new Account(100);
account.Put(20); // добавляем на счет 20
Console.WriteLine($"Сумма на счете: {account.Sum}");
account.Take(70); // пытаемся снять со счета 70
Console.WriteLine($"Сумма на счете: {account.Sum}");
account.Take(180); // пытаемся снять со счета 180
Console.WriteLine($"Сумма на счете: {account.Sum}");
Консольный вывод:
Сумма на счете: 120
Сумма на счете: 50
Сумма на счете: 50

308.

public void Put(int sum)
{
Sum += sum;
Console.WriteLine($"На счет поступило: {sum}");
}

309.

Определение и вызов событий
События объявляются в классе с помощью
ключевого слова event, после которого
указывается тип делегата, который
представляет событие:
delegate void AccountHandler(string message);
event AccountHandler Notify;

310.

Notify("Произошло действие");
_______________________________________________
if(Notify !=null) Notify("Произошло действие");
Или так:
Notify?.Invoke("Произошло действие");

311.

class Account {
public delegate void AccountHandler(string message);
public event AccountHandler? Notify;
// 1.Определение события
public Account(int sum) => Sum = sum;
public int Sum { get; private set; }
public void Put(int sum) {
Sum += sum;
Notify?.Invoke($"На счет поступило: {sum}"); // 2.Вызов события
}
public void Take(int sum) {
if (Sum >= sum) {
Sum -= sum;
Notify?.Invoke($"Со счета снято: {sum}"); // 2.Вызов события
}
else {
Notify?.Invoke($"Недостаточно денег на счете. Текущий баланс: {Sum}"); ;
}}}

312.

Добавление обработчика события
Notify += обработчик события;
Определим обработчики для события Notify, чтобы получить в программе нужные
уведомления:
Account account = new Account(100);
account.Notify += DisplayMessage; // Добавляем обработчик для события Notify
account.Put(20); // добавляем на счет 20
Console.WriteLine($"Сумма на счете: {account.Sum}"); На счет поступило: 20
Сумма на счете: 120
account.Take(70); // пытаемся снять со счета 70
Со счета снято: 70
Console.WriteLine($"Сумма на счете: {account.Sum}"); Сумма на счете: 50
Недостаточно денег на счете. Текущий баланс: 50
account.Take(180); // пытаемся снять со счета 180
Console.WriteLine($"Сумма на счете: {account.Sum}"); Сумма на счете: 50
void DisplayMessage(string message) => Console.WriteLine(message);

313.

Добавление и удаление обработчиков
Account account = new Account(100);
account.Notify += DisplayMessage;
// добавляем обработчик DisplayMessage
account.Notify += DisplayRedMessage; // добавляем обработчик DisplayMessage
account.Put(20); // добавляем на счет 20
account.Notify -= DisplayRedMessage; // удаляем обработчик DisplayRedMessage
account.Put(50); // добавляем на счет 50
void DisplayMessage(string message) => Console.WriteLine(message);
void DisplayRedMessage(string message)
{
// Устанавливаем красный цвет символов
Консольный вывод:
Console.ForegroundColor = ConsoleColor.Red;
На счет поступило: 20
Console.WriteLine(message);
На счет поступило: 20
// Сбрасываем настройки цвета
На счет поступило: 50
Console.ResetColor();
}

314.

Account acc = new Account(100);
// установка делегата, который указывает на метод DisplayMessage
acc.Notify += new Account.AccountHandler(DisplayMessage);
// установка в качестве обработчика метода DisplayMessage
acc.Notify += DisplayMessage;
// добавляем обработчик
DisplayMessage
acc.Put(20); // добавляем на счет 20
void DisplayMessage(string message) => Console.WriteLine(message);

315.

Account acc = new Account(100);
acc.Notify += delegate (string mes)
{
Console.WriteLine(mes);
};
acc.Put(20);

316.

Account account = new Account(100);
account.Notify += message => Console.WriteLine(message);
account.Put(20);

317.

Управление обработчиками
class Account
{
public delegate void AccountHandler(string message);
AccountHandler? notify;
public event AccountHandler Notify
{
add
{
notify += value;
Console.WriteLine($"{value.Method.Name} добавлен");
}
remove
{
notify -= value;
Console.WriteLine($"{value.Method.Name} удален");
}
}
public Account(int sum) => Sum = sum;
public int Sum { get; private set; }
public void Put(int sum)
{
Sum += sum;
notify?.Invoke($"На счет поступило: {sum}"); // 2.Вызов
события
}
public void Take(int sum)
{
if (Sum >= sum)
{
Sum -= sum;
notify?.Invoke($"Со счета снято: {sum}"); // 2.Вызов события
}
else
{
notify?.Invoke($"Недостаточно денег на счете. Текущий
баланс: {Sum}"); ;
}
}
}

318.

Account acc = new Account(100);
acc.Notify += DisplayMessage;
// добавляем обработчик
DisplayMessage
acc.Put(20); // добавляем на счет 20
acc.Notify -= DisplayMessage; // удаляем обработчик
DisplayRedMessage
acc.Put(20); // добавляем на счет 20
void DisplayMessage(string message) => Console.WriteLine(message);
Консольный вывод программы:
DisplayMessage добавлен
На счет поступило: 20
DisplayMessage удален

319.

Передача данных события
class AccountEventArgs
{
// Сообщение
public string Message{get;}
// Сумма, на которую изменился счет
public int Sum {get;}
public AccountEventArgs(string message, int sum)
{
Message = message;
Sum = sum;
}
}

320.

class Account {
public delegate void AccountHandler(Account sender, AccountEventArgs e);
public event AccountHandler? Notify;
public int Sum { get; private set; }
public Account(int sum) => Sum = sum;
public void Put(int sum) {
Sum += sum;
Notify?.Invoke(this, new AccountEventArgs($"На счет поступило {sum}", sum));
}
public void Take(int sum) {
if (Sum >= sum) {
Sum -= sum;
Notify?.Invoke(this, new AccountEventArgs($"Сумма {sum} снята со счета", sum));
}
else {
Notify?.Invoke(this, new AccountEventArgs("Недостаточно денег на счете", sum));
}
}
}

321.

Account acc = new Account(100);
acc.Notify += DisplayMessage;
acc.Put(20);
acc.Take(70);
acc.Take(150);
void DisplayMessage(Account sender, AccountEventArgs e)
{
Console.WriteLine($"Сумма транзакции: {e.Sum}");
Console.WriteLine(e.Message);
Console.WriteLine($"Текущая сумма на счете: {sender.Sum}");
}

322.

Ковариантность и
контравариантность делегатов
Делегаты могут быть ковариантными и
контравариантными. Ковариантность делегата
предполагает, что возвращаемым типом может быть
производный тип. Контрвариантность делегата
предполагает, что типом параметра может быть более
универсальный тип.

323.

class Message
{
public string Text { get; }
public Message(string text) => Text = text;
public virtual void Print() => Console.WriteLine($"Message: {Text}");
}
class EmailMessage: Message
{
public EmailMessage(string text): base(text) { }
public override void Print() => Console.WriteLine($"Email: {Text}");
}
class SmsMessage : Message
{
public SmsMessage(string text) : base(text) { }
public override void Print() => Console.WriteLine($"Sms: {Text}");
}

324.

Ковариантность
// делегату с базовым типом передаем метод с производным типом
MessageBuilder messageBuilder = WriteEmailMessage; // ковариантность
Message message = messageBuilder("Hello");
message.Print(); // Email: Hello
EmailMessage WriteEmailMessage(string text) => new EmailMessage(text);
delegate Message MessageBuilder(string text);

325.

Контрвариантность
// делегату с производным типом передаем метод с базовым
типом
EmailReceiver emailBox = ReceiveMessage; // контравариантность
emailBox(new EmailMessage("Welcome")); // Email: Welcome
void ReceiveMessage(Message message) => message.Print();
delegate void EmailReceiver(EmailMessage message);

326.

Ковариантность и контравариантность
в обобщенных делегатах
// ковариантность
MessageBuilder<Message> messageBuilder = WriteEmailMessage;
Message message = messageBuilder("hello Tom"); // вызов делегата
message.Print(); // Email: hello Tom
EmailMessage WriteEmailMessage(string text) => new
EmailMessage(text);
delegate T MessageBuilder<out T>(string text);

327.

MessageReceiver<EmailMessage> messageReceiver = ReceiveMessage;
// контравариантность
messageReceiver(new EmailMessage("Hello World!"));
void ReceiveMessage(Message message) => message.Print();
delegate void MessageReceiver<in T>(T message);

328.

MessageConverter<SmsMessage, Message> converter = ConvertToEmail;
Message message = converter(new SmsMessage("Delegates"));
message.Print(); // Email: Delegates
EmailMessage ConvertToEmail(Message message) => new
EmailMessage(message.Text);
delegate E MessageConverter<in M, out E>(M message);

329.

Делегаты Action, Predicate и Func
Делегат Action представляет некоторое действие,
которое ничего не возвращает, то есть в качестве
возвращаемого типа имеет тип void:
public delegate void Action()
public delegate void Action<in T>(T obj)

330.

DoOperation(10, 6, Add);
// 10 + 6 = 16
DoOperation(10, 6, Multiply); // 10 * 6 = 60
void DoOperation(int a, int b, Action<int, int> op) => op(a, b);
void Add(int x, int y) => Console.WriteLine($"{x} + {y} = {x + y}");
void Multiply(int x, int y) => Console.WriteLine($"{x} * {y} = {x * y}");

331.

Делегат Predicate<T> принимает один параметр и
возвращает значение типа bool:
delegate bool Predicate<in T>(T obj);
__________________________________________________
Predicate<int> isPositive = (int x) => x > 0;
Console.WriteLine(isPositive(20));
Console.WriteLine(isPositive(-20));

332.

Еще одним распространенным делегатом является Func. Он
возвращает результат действия и может принимать параметры. Он
также имеет различные формы: от Func<out T>(), где T - тип
возвращаемого значения, до Func<in T1, in T2,...in T16, out TResult>(),
то есть может принимать до 16 параметров.
TResult Func<out TResult>()
TResult Func<in T, out TResult>(T arg)
TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2)
TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3)
TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3
arg3, T4 arg4)
//...........................................

333.

int result1 = DoOperation(6, DoubleNumber); // 12
Console.WriteLine(result1);
int result2 = DoOperation(6, SquareNumber); // 36
Console.WriteLine(result2);
int DoOperation(int n, Func<int, int> operation) => operation(n);
int DoubleNumber(int n) => 2 * n;
int SquareNumber(int n) => n * n;

334.

Func<int, int, string> createString = (a, b) => $"{a}{b}";
Console.WriteLine(createString(1, 5)); // 15
Console.WriteLine(createString(3, 5)); // 35

335.

Замыкания
Замыкание (closure) представляет объект функции, который запоминает
свое лексическое окружение даже в том случае, когда она выполняется
вне своей области видимости.
Технически замыкание включает три компонента:
• внешняя функция, которая определяет некоторую область видимости и
в которой определены некоторые переменные и параметры лексическое окружение
• переменные и параметры (лексическое окружение), которые
определены во внешней функции
• вложенная функция, которая использует переменные и параметры
внешней функции
В языке C# реализовать замыкания можно разными способами - с
помощью локальных функций и лямбда-выражений.

336.

var fn = Outer(); // fn = Inner, так как метод Outer возвращает функцию Inner
// вызываем внутреннюю функцию Inner
fn(); // 6
fn(); // 7
fn(); // 8
Action Outer() // метод или внешняя функция
{
int x = 5; // лексическое окружение - локальная переменная
void Inner() // локальная функция
{
x++;
// операции с лексическим окружением
Console.WriteLine(x);
}
return Inner; // возвращаем локальную функцию
}

337.

Реализация с помощью лямбда-выражений
var outerFn = () =>
{
int x = 10;
var innerFn = () => Console.WriteLine(++x);
return innerFn;
};
var fn = outerFn(); // fn = innerFn, так как outerFn возвращает innerFn
// вызываем innerFn
fn(); // 11
fn(); // 12
fn(); // 13

338.

Применение параметров
var fn = Multiply(5);
Console.WriteLine(fn(5)); // 25
Console.WriteLine(fn(6)); // 30
Console.WriteLine(fn(7)); // 35
Operation Multiply(int n)
{
int Inner(int m)
{
return n * m;
}
return Inner;
}
delegate int Operation(int n);

339.

var multiply = (int n) => (int m) => n * m;
var fn = multiply(5);
Console.WriteLine(fn(5)); // 25
Console.WriteLine(fn(6)); // 30
Console.WriteLine(fn(7)); // 35

340.

Интерфейсы
Для определения интерфейса используется ключевое слово interface.
Как правило, названия интерфейсов в C# начинаются с заглавной
буквы I, например, IComparable, IEnumerable, однако это не
обязательное требование, а больше стиль программирования.
В целом, интерфейсы могут определять следующие сущности:
Методы
Свойства
Индексаторы
События
Статические поля и константы (начиная с версии C# 8.0)

341.

interface IMovable
{
// константа
const int minSpeed = 0; // минимальная скорость
// статическая переменная
static int maxSpeed = 60; // максимальная скорость
// метод
void Move();
// движение
// свойство
string Name { get; set; } // название
delegate void MoveHandler(string message); // определение делегата для
события
// событие
event MoveHandler MoveEvent; // событие движения
}

342.

Реализация по умолчанию
interface IMovable
{
// реализация метода по умолчанию
void Move() => Console.WriteLine("Walking");
// реализация свойства по умолчанию
// свойство только для чтения
int MaxSpeed { get { return 0; } }
}

343.

Console.WriteLine(IMovable.MaxSpeed); // 60
IMovable.MaxSpeed = 65;
Console.WriteLine(IMovable.MaxSpeed); // 65
double time = IMovable.GetTime(500, 10);
Console.WriteLine(time); // 50
interface Imovable {
public const int minSpeed = 0; // минимальная скорость
private static int maxSpeed = 60; // максимальная скорость
// находим время, за которое надо пройти расстояние distance со скоростью speed
static double GetTime(double distance, double speed) => distance / speed;
static int MaxSpeed {
get => maxSpeed;
set {
if (value > 0) maxSpeed = value;
}
}
}

344.

Добавление интерфейса

345.

Применение интерфейсов
IMovable m = new IMovable(); // ! Ошибка, так
сделать нельзя
interface IMovable
{
void Move();
}

346.

// применение интерфейса в классе
class Person : IMovable
{
public void Move()
{
Console.WriteLine("Человек идет");
}
}
// применение интерфейса в структуре
struct Car : IMovable
{
public void Move()
{
Console.WriteLine("Машина едет");
}
}

347.

Person person = new Person();
Car car = new Car();
DoAction(person);
DoAction(car);
void DoAction(IMovable movable) => movable.Move();
interface IMovable
{
void Move();
}
class Person : IMovable
{
public void Move() => Console.WriteLine("Человек идет");
}
struct Car : IMovable
{
public void Move() => Console.WriteLine("Машина едет");
}
Консольный вывод данной программы:
Человек идет
Машина едет

348.

Реализация интерфейсов по умолчанию
IMovable tom = new Person();
Person tom = new Person();
Car tesla = new Car();
tom.Move(); // Ошибка - метод Move не определен в классе Person
tom.Move(); // Walking
tesla.Move(); // Driving
interface IMovable
{
void Move() => Console.WriteLine("Walking");
}
class Person : IMovable { }
class Car : IMovable
{
public void Move() => Console.WriteLine("Driving");
}

349.

Множественная реализация интерфейсов
class myClass: myInterface1, myInterface2, myInterface3, ...
{
}

350.

Message hello = new Message("Hello World");
hello.Print(); // Hello World
interface IMessage
{
string Text { get; set; }
}
interface IPrintable
{
void Print();
}
class Message : IMessage, IPrintable
{
public string Text { get; set; }
public Message(string text) => Text = text;
public void Print()=> Console.WriteLine(Text);
}

351.

Явная реализация интерфейсов
interface IAction
{
void Move();
}
class BaseAction : IAction
{
void IAction.Move() => Console.WriteLine("Move in Base Class");
}

352.

BaseAction baseAction1 = new BaseAction();
// baseAction1.Move(); // ! Ошибка - в BaseAction нет метода Move
// необходимо приведение к типу IAction
// небезопасное приведение
((IAction)baseAction1).Move();
// безопасное приведение
if (baseAction1 is IAction action) action.Move();
// или так
IAction baseAction2 = new BaseAction();
baseAction2.Move();

353.

class Person : ISchool, IUniversity
{
public void Study() => Console.WriteLine("Учеба в школе или в
университете");
}
interface ISchool
{
void Study();
}
interface IUniversity
{
void Study();
}

354.

class Person : ISchool, IUniversity
{
void ISchool.Study() => Console.WriteLine("Учеба в школе");
void IUniversity.Study() => Console.WriteLine("Учеба в университете");
}
Использование:
Person person = new Person();
((ISchool)person).Study();
((IUniversity)person).Study();

355.

interface IAction
{
void Move();
}
class BaseAction : IAction
{
public void Move() =>Console.WriteLine("Move in BaseAction");
}
class HeroAction : BaseAction, IAction
{
void IAction.Move() => Console.WriteLine("Move in HeroAction");
}

356.

HeroAction action1 = new HeroAction();
action1.Move();
// Move in BaseAction
((IAction)action1).Move(); // Move in HeroAction
IAction action2 = new HeroAction();
action2.Move();
// Move in HeroAction

357.

Наследование интерфейсов
interface IAction
{
void Move();
}
interface IRunAction : IAction
{
void Run();
}
class BaseAction : IRunAction
{
public void Move()
{
Console.WriteLine("Move");
}
public void Run()
{
Console.WriteLine("Run");
}
}

358.

IAction action1 = new RunAction();
action1.Move(); // I am moving
IRunAction action2 = new RunAction();
action2.Move(); // I am running
interface IAction
{
void Move() => Console.WriteLine("I am moving");
}
interface IRunAction : IAction
{
// скрываем реализацию из IAction
new void Move() => Console.WriteLine("I am running");
}
class RunAction : IRunAction { }

359.

IAction action1 = new RunAction();
action1.Move(); // I am tired
IRunAction action2 = new RunAction();
action2.Move(); // I am tired
interface IAction
{
void Move() => Console.WriteLine("I am moving");
}
interface IRunAction : IAction
{
new void Move() => Console.WriteLine("I am running");
}
class RunAction : IRunAction
{
public void Move() => Console.WriteLine("I am tired");
}

360.

public interface IAction
{
void Move();
}
internal interface IRunAction : IAction
{
void Run();
}

361.

Копирование объектов. Интерфейс ICloneable
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}

362.

var tom = new Person("Tom", 23);
var bob = tom;
bob.Name = "Bob";
Console.WriteLine(tom.Name); // Bob
__________________________________________________
public interface ICloneable
{
object Clone();
}

363.

Поверхностное копирование
class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
public object Clone()
{
return new Person(Name, Age);
}
}

364.

var tom = new Person("Tom", 23);
var bob = (Person)tom.Clone();
bob.Name = "Bob";
Console.WriteLine(tom.Name); // Tom

365.

class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
public object Clone()
{
return MemberwiseClone();
}
}

366.

class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Company Work { get; set; }
public Person(string name, int age, Company company)
{
Name = name;
Age = age;
Work = company;
}
public object Clone() => MemberwiseClone();
}
class Company
{
public string Name { get; set; }
public Company(string name) => Name = name;
}

367.

var tom = new Person("Tom", 23, new Company("Microsoft"));
var bob = (Person)tom.Clone();
bob.Work.Name = "Google";
Console.WriteLine(tom.Work.Name); // Google - а должно
быть Microsoft

368.

Глубокое копирование
class Person : ICloneable {
public string Name { get; set; }
public int Age { get; set; }
public Company Work { get; set; }
public Person(string name, int age, Company company) {
Name = name;
Age = age;
Work = company;
}
public object Clone() => new Person(Name, Age, new Company(Work.Name));
}
class Company {
public string Name { get; set; }
public Company(string name) => Name = name;
}

369.

Список List<T>
Мы можем создать пустой список:
List<string> people = new List<string>();
В данном случае объект List типизируется типом string. А это значит, что
хранить в этом списке мы можем только строки.
Можно сразу при создании списка инициализировать его начальными
значениями. В этом случае элементы списка помещаются после вызова
конструктора в фигурных скобках
List<string> people = new List<string>() { "Tom", "Bob", "Sam" };
В данном случае в список помещаются три строки

370.

var people = new List<string>() { "Tom", "Bob", "Sam" };
var employees = new List<string>(people);
Можно совместить оба способа:
var people = new List<string>() { "Tom", "Bob", "Sam" };
var employees = new List<string>(people){"Mike"};

371.

List<Person> people = new List<Person>()
{
new Person("Tom"),
new Person("Bob"),
new Person("Sam")
};
class Person
{
public string Name { get;}
public Person(string name) => Name = name;
}

372.

Установка начальной емкости
списка
List<string> people = new List<string>(16);

373.

Обращение к элементам списка
var people = new List<string>() { "Tom", "Bob", "Sam" };
string firstPerson = people[0]; // получаем первый элемент
Console.WriteLine(firstPerson); // Tom
people[0] = "Mike"; // изменяем первый элемент
Console.WriteLine(people[0]); // Mike

374.

Длина списка
С помощью свойства Count можно получить длину списка:
var people = new List<string>() { "Tom", "Bob", "Sam" };
Console.WriteLine(people.Count); // 3

375.

Перебор списка
var people = new List<string>() { "Tom", "Bob", "Sam" };
foreach (var person in people)
{
Console.WriteLine(person);
}
Вывод программы:
Tom
Bob
Sam

376.

var people = new List<string>() { "Tom", "Bob", "Sam" };
for (int i = 0; i < people.Count; i++)
{
Console.WriteLine(people[i]);
}

377.

Методы списка
void Add(T item): добавление нового элемента в список
void AddRange(IEnumerable<T> collection): добавление в список
коллекции или массива
int BinarySearch(T item): бинарный поиск элемента в списке. Если
элемент найден, то метод возвращает индекс этого элемента в
коллекции. При этом список должен быть отсортирован.
void CopyTo(T[] array): копирует список в массив array
void CopyTo(int index, T[] array, int arrayIndex, int count): копирует из
списка начиная с индекса index элементы, количество которых равно
count, и вставляет их в массив array начиная с индекса arrayIndex
bool Contains(T item): возвращает true, если элемент item есть в списке
void Clear(): удаляет из списка все элементы
bool Exists(Predicate<T> match): возвращает true, если в списке есть
элемент, который соответствует делегату match

378.

T? Find(Predicate<T> match): возвращает первый элемент, который
соответствует делегату match. Если элемент не найден, возвращается
null
T? FindLast(Predicate<T> match): возвращает последний элемент,
который соответствует делегату match. Если элемент не найден,
возвращается null
List<T> FindAll(Predicate<T> match): возвращает список элементов,
которые соответствуют делегату match
int IndexOf(T item): возвращает индекс первого вхождения элемента в
списке
int LastIndexOf(T item): возвращает индекс последнего вхождения
элемента в списке
List<T> GetRange(int index, int count): возвращает список элементов,
количество которых равно count, начиная с индекса index.
void Insert(int index, T item): вставляет элемент item в список по индексу
index. Если такого индекса в списке нет, то генерируется исключение
void InsertRange(int index, collection): вставляет коллекцию элементов
collection в текущий список начиная с индекса index. Если такого индекса
в списке нет, то генерируется исключение

379.

• bool Remove(T item): удаляет элемент item из списка, и если
удаление прошло успешно, то возвращает true. Если в списке
несколько одинаковых элементов, то удаляется только первый из
них
• void RemoveAt(int index): удаление элемента по указанному
индексу index. Если такого индекса в списке нет, то генерируется
исключение
• void RemoveRange(int index, int count): параметр index задает
индекс, с которого надо удалить элементы, а параметр count задает
количество удаляемых элементов.
• int RemoveAll((Predicate<T> match)): удаляет все элементы,
которые соответствуют делегату match. Возвращает количество
удаленных элементов
• void Reverse(): изменяет порядок элементов
• void Reverse(int index, int count): изменяет порядок на обратный
для элементов, количество которых равно count, начиная с индекса
index
• void Sort(): сортировка списка
• void Sort(IComparer<T>? comparer): сортировка списка с
помощью объекта comparer, который передается в качестве
параметра

380.

Добавление в список
List<string> people = new List<string> () { "Tom" };
people.Add("Bob"); // добавление элемента // people = { "Tom", "Bob" };
people.AddRange(new[] { "Sam", "Alice" }); // добавляем массив
// people = { "Tom", "Bob", "Sam", "Alice" };
// также можно было бы добавить другой список
// people.AddRange(new List<string>(){ "Sam", "Alice" });
people.Insert(0, "Eugene"); // вставляем на первое место // people = { "Eugene", "Tom", "Bob", "Sam",
"Alice" };
people.InsertRange(1, new string[] {"Mike", "Kate"}); // вставляем массив с индекса 1
// people = { "Eugene", "Mike", "Kate", "Tom", "Bob", "Sam", "Alice" };
// также можно было бы добавить другой список
// people.InsertRange(1, new List<string>(){ "Mike", "Kate" });
</string></string>

381.

Удаление из списка
var people = new List<string> () { "Eugene", "Mike", "Kate", "Tom", "Bob", "Sam", "Tom",
"Alice" };
people.RemoveAt(1); // удаляем второй элемент // people = { "Eugene", "Kate", "Tom",
"Bob", "Sam", "Tom", "Alice" };
people.Remove("Tom"); // удаляем элемент "Tom" // people = { "Eugene", "Kate",
"Bob", "Sam", "Tom", "Alice" };
// удаляем из списка все элементы, длина строки которых равна 3
people.RemoveAll(person => person.Length == 3); // people = { "Eugene", "Kate", "Alice" };
// удаляем из списка 2 элемента начиная с индекса 1
people.RemoveRange(1, 2); // people = { "Eugene"};
// полностью очищаем список
people.Clear(); // people = { };

382.

Поиск и проверка элемента
var people = new List<string> () { "Eugene", "Mike", "Kate", "Tom", "Bob", "Sam" };
var containsBob = people.Contains("Bob");
// true
var containsBill = people.Contains("Bill"); // false
var existsLength3 = people.Exists(p => p.Length == 3); // true // проверяем, есть ли в списке строки с длиной 3 символа
var existsLength7 = people.Exists(p => p.Length == 7); // false // проверяем, есть ли в списке строки с длиной 7 символов
var firstWithLength3 = people.Find(p => p.Length == 3); // Tom // получаем первый элемент с длиной в 3 символа
var lastWithLength3 = people.FindLast(p => p.Length == 3); // Sam // получаем последний элемент с длиной в 3 символа
List<string> peopleWithLength3 = people.FindAll(p => p.Length == 3); // получаем все элементы с длиной в 3 символа в
виде списка
// peopleWithLength3 { "Tom", "Bob", "Sam"}

383.

Получение диапазона и копирование в массив
List<string> people = new List<string>() {"Eugene", "Tom", "Mike", "Sam",
"Bob" };
// получаем диапазон со второго по четвертый элемент
var range = people.GetRange(1, 3);
// range = { "Tom", "Mike", "Sam"};
// копируем в массив первые три элемента
string[] partOfPeople = new string[3];
people.CopyTo(0, partOfPeople, 0, 3);
// partOfPeople = { "Eugene", "Tom", "Mike"};

384.

Расположение элементов в обратном порядке
var people = new List<string> () { "Eugene", "Tom", "Mike", "Sam", "Bob" };
// переворачиваем весь список
people.Reverse();
// people = { "Bob","Sam", "Mike", "Tom", "Eugene"};
var people2 = new List<string>() { "Eugene", "Tom", "Mike", "Sam", "Bob" };
// переворачиваем часть только 3 элемента с индекса 1
people2.Reverse(1, 3);
// people2 = { "Eugene","Sam", "Mike", "Tom", "Bob" };

385.

Двухсвязный список LinkedList<T>
LinkedList<string> people = new LinkedList<string>();
_____________________________________________
var employees = new List<string> { "Tom", "Sam", "Bob" };
LinkedList<string> people = new LinkedList<string>(employees);
foreach (string person in people)
{
Console.WriteLine(person);
}

386.

LinkedListNode
Класс LinkedListNode имеет следующие свойства:
Value: возвращает или устанавливает само значение узла,
представленное типом T
Next: возвращает ссылку на следующий элемент типа
LinkedListNode<T> в списке. Если следующий элемент
отсутствует, то имеет значение null
Previous: возвращает ссылку предыдущий элемент типа
LinkedListNode<T> в списке. Если предыдущий элемент
отсутствует, то имеет значение null

387.

Свойства LinkedList
Класс LinkedList определяет следующие
свойства:
Count: количество элементов в связанном
списке
First: первый узел в списке в виде объекта
LinkedListNode<T>
Last: последний узел в списке в виде объекта
LinkedListNode<T>

388.

var employees = new List<string> { "Tom", "Sam", "Bob" };
LinkedList<string> people = new
LinkedList<string>(employees);
Console.WriteLine(people.Count);
// 3
Console.WriteLine(people.First?.Value); // Tom
Console.WriteLine(people.Last?.Value); // Bob

389.

LinkedList<string> people = new LinkedList<string>(new[] { "Tom", "Sam", "Bob" });
// от начала до конца списка
var currentNode = people.First;
while(currentNode != null)
{
Console.WriteLine(currentNode.Value);
currentNode = currentNode.Next;
}
// с конца до начала списка
currentNode = people.Last;
while (currentNode != null)
{
Console.WriteLine(currentNode.Value);
currentNode = currentNode.Previous;
}

390.

Методы LinkedList
• AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode):
вставляет узел newNode в список после узла node.
• AddAfter(LinkedListNode<T> node, T value): вставляет в список новый
узел со значением value после узла node.
• AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode):
вставляет в список узел newNode перед узлом node.
• AddBefore(LinkedListNode<T> node, T value): вставляет в список
новый узел со значением value перед узлом node.
• AddFirst(LinkedListNode<T> node): вставляет новый узел в начало
списка
• AddFirst(T value): вставляет новый узел со значением value в начало
списка
• AddLast(LinkedListNode<T> node): вставляет новый узел в конец
списка
• AddLast(T value): вставляет новый узел со значением value в конец
списка
• RemoveFirst(): удаляет первый узел из списка. После этого новым
первым узлом становится узел, следующий за удаленным
• RemoveLast(): удаляет последний узел из списка

391.

var people = new LinkedList<string>();
people.AddLast("Tom"); // вставляем узел со значением Tom на
последнее место
//так как в списке нет узлов, то последнее будет
также и первым
people.AddFirst("Bob"); // вставляем узел со значением Bob на первое
место
// вставляем после первого узла новый узел со значением Mike
if (people.First != null) people.AddAfter(people.First, "Mike");
// теперь у нас список имеет следующую последовательность: Bob Mike
Tom
foreach (var person in people) Console.WriteLine(person);

392.

var company = new LinkedList<Person>();
company.AddLast(new Person("Tom"));
company.AddLast(new Person("Sam"));
company.AddFirst(new Person("Bill"));
foreach (var person in company) Console.WriteLine(person.Name);
class Person
{
public string Name { get; }
public Person(string name) => Name = name;
}

393.

Очередь Queue
Queue<string> people = new Queue<string>();
При создании пустой очереди можно указать
емкость очереди:
Queue<string> people = new Queue<string>(16);

394.

var employees = new List<string> { "Tom", "Sam", "Bob" };
Queue<string> people = new Queue<string>(employees);
foreach (var person in people) Console.WriteLine(person);
Console.WriteLine(people.Count); // 3

395.

Методы Queue
У класса Queue<T> можно отметить следующие методы:
void Clear(): очищает очередь
bool Contains(T item): возвращает true, если элемент item
имеется в очереди
T Dequeue(): извлекает и возвращает первый элемент
очереди
void Enqueue(T item): добавляет элемент в конец очереди
T Peek(): просто возвращает первый элемент из начала
очереди без его удаления

396.

var people = new Queue<string>();
// добавляем элементы
people.Enqueue("Tom"); // people = { Tom }
people.Enqueue("Bob"); // people = { Tom, Bob }
people.Enqueue("Sam"); // people = { Tom, Bob, Sam }
// получаем элемент из самого начала очереди
var firstPerson = people.Peek();
Console.WriteLine(firstPerson); // Tom
// удаляем элементы
var person1 = people.Dequeue(); // people = { Bob, Sam }
Console.WriteLine(person1); // Tom
var person2 = people.Dequeue(); // people = { Sam }
Console.WriteLine(person2); // Bob
var person3 = people.Dequeue(); // people = { }
Console.WriteLine(person3); // Sam

397.

if(people.Count > 0)
{
var person = people.Peek();
people.Dequeue();
}
bool TryDequeue(out T result): передает в переменную result первый элемент
очереди с его удалением из очереди, возвращает true, если очередь не пуста и
элемент успешно получен.
bool TryPeek(out T result): передает в переменную result первый элемент
очереди без его извлечения из очереди, возвращает true, если очередь не пуста
и элемент успешно получен.

398.

var people = new Queue<string>();
// добавляем элементы
people.Enqueue("Tom"); // people = { Tom }
// удаляем элементы
var success1 = people.TryDequeue(out var person1); // success1 = true
if (success1) Console.WriteLine(person1); // Tom
var success2 = people.TryPeek(out var person2); // success2 = false
if (success2) Console.WriteLine(person2);

399.

var patients = new Queue<Person>();
patients.Enqueue(new Person("Tom"));
patients.Enqueue(new Person("Bob"));
patients.Enqueue(new Person("Sam"));
var practitioner = new Doctor();
practitioner.TakePatients(patients);
Консольный вывод:
Осмотр пациента Tom
Осмотр пациента Bob
Осмотр пациента Sam
Доктор закончил осматривать пациентов
class Person {
public string Name { get; }
public Person(string name) => Name = name;
}
class Doctor {
public void TakePatients(Queue<Person> patients) {
while(patients.Count > 0) {
var patient = patients.Dequeue();
Console.WriteLine($"Осмотр пациента {patient.Name}");
}
Console.WriteLine("Доктор закончил осматривать пациентов");
}
}

400.

Коллекция Stack<T>
Stack<string> people = new Stack<string>();
При создании пустого стека можно указать емкость стека:
Stack<string> people = new Stack<string>(16);
Также можно инициализировать стек элементами из другой коллекции
или массивом:
var employees = new List<string> { "Tom", "Sam", "Bob" };
Stack<string> people = new Stack<string>(employees);
foreach (var person in people) Console.WriteLine(person);
Console.WriteLine(people.Count); // 3

401.

Методы Stack
В классе Stack можно выделить следующие методы:
Clear: очищает стек
Contains: проверяет наличие в стеке элемента и
возвращает true при его наличии
Push: добавляет элемент в стек в верхушку стека
Pop: извлекает и возвращает первый элемент из
стека
Peek: просто возвращает первый элемент из стека
без его удаления

402.

var people = new Stack<string>();
people.Push("Tom"); // people = { Tom }
people.Push("Sam"); // people = { Sam, Tom }
people.Push("Bob"); // people = { Bob, Sam, Tom }
// получаем первый элемент стека без его удаления
string headPerson = people.Peek();
Console.WriteLine(headPerson); // Bob
string person1 = people.Pop(); // people = { Sam, Tom }
Console.WriteLine(person1); // Bob
string person2 = people.Pop(); // people = { Tom }
Console.WriteLine(person2); // Sam
string person3 = people.Pop(); // people = { }
Console.WriteLine(person3); // Tom

403.

404.

if(people.Count > 0)
{
var person = people.Peek();
people.Pop();
}
bool TryPop(out T result): удаляет из стека первый элемент и
передает его в переменную result, возвращает true, если очередь не
пуста и элемент успешно получен.
bool TryPeek(out T result): передает в переменную result первый
элемент стека без его извлечения, возвращает true, если элемент
успешно получен.

405.

var people = new Stack<string>();
people.Push("Tom");
// people = { Tom }
// удаляем элементы
var success1 = people.TryPop(out var person1); // success1 = true
if (success1) Console.WriteLine(person1); // Tom
var success2 = people.TryPeek(out var person2); // success2 = false
if (success2) Console.WriteLine(person2);

406.

Коллекция Dictionary<K, V>
Еще один распространенный тип коллекции представляют словари. Словарь
хранит объекты, которые представляют пару ключ-значение. Класс словаря
Dictionary<K, V> типизируется двумя типами: параметр K представляет тип
ключей, а параметр V предоставляет тип значений.
Dictionary<int, string> people = new Dictionary<int, string>();
При определении словаря его сразу же можно инициализировать значениями:
var people = new Dictionary<int, string>()
{
{ 5, "Tom"},
[5] = "Tom",
{ 3, "Sam"},
{ 11, "Bob"}
};
[6] = "Sam",
[7] = "Bob"

407.

KeyValuePair
Стоит отметить, что каждый элемент в словаре
представляет структуру KeyValuePair<TKey, TValue>, где
параметр TKey представляет тип ключа, а
параметр TValue - тип значений элементов.
var mike = new KeyValuePair<int, string>(56, "Mike");
var employees = new List<KeyValuePair<int, string>>() { mike};
var people = new Dictionary<int, string>(employees);

408.

var mike = new KeyValuePair<int, string>(56, "Mike");
var employees = new List<KeyValuePair<int, string>>() {
mike };
var people = new Dictionary<int, string>(employees)
{
[5] = "Tom",
[6] = "Sam",
[7] = "Bob",
};

409.

Перебор словаря
Для перебора словаря можно применять цикл foreach:
var people = new Dictionary<int, string>()
{
[5] = "Tom",
key: 5 value: Tom
[6] = "Sam",
key: 6 value: Sam
[7] = "Bob"
key: 7 value: Bob
};
foreach(var person in people)
{
Console.WriteLine($"key: {person.Key} value: {person.Value}");
}

410.

Получение элементов
var people = new Dictionary<int, string>()
{
[5] = "Tom",
[6] = "Sam",
[7] = "Bob",
};
// получаем элемент по ключу 6
string sam = people[6]; // Sam
Console.WriteLine(sam); // Sam
// переустанавливаем значение по ключу 6
people[6] = "Mike";
Console.WriteLine(people[6]); // Mike
// добавляем новый элемент по ключу 22
people[22] = "Eugene";
Console.WriteLine(people[22]); // Eugene

411.

Методы и свойства Dictionary
• void Add(K key, V value): добавляет новый элемент в словарь
• void Clear(): очищает словарь
• bool ContainsKey(K key): проверяет наличие элемента с определенным
ключом и возвращает true при его наличии в словаре
• bool ContainsValue(V value): проверяет наличие элемента с
определенным значением и возвращает true при его наличии в
словаре
• bool Remove(K key): удаляет по ключу элемент из словаря
• Другая версия этого метода позволяет получить удленный элемент в
выходной параметр: bool Remove(K key, out V value)
• bool TryGetValue(K key, out V value): получает из словаря элемент по
ключу key. При успешном получении передает значение элемента в
выходной параметр value и возвращает true
• bool TryAdd(K key, V value): добавляет в словарь элемент с ключом key
и значением value. При успешном добавлении возвращает true

412.

var phoneBook = new Dictionary<string, string>(); // условная телефонная книга
// добавляем элемент: ключ - номер телефона, значение - имя абонента
phoneBook.Add("+123456", "Tom");
// phoneBook["+123456"] = "Tom"; // альтернативное добавление
// Проверка наличия
var phoneExists1 = phoneBook.ContainsKey("+123456"); // true
Console.WriteLine($"+123456: {phoneExists1}");
var phoneExists2 = phoneBook.ContainsKey("+567456"); // false
Console.WriteLine($"+567456: {phoneExists2}");
var abonentExists1 = phoneBook.ContainsValue("Tom"); // true
Console.WriteLine($"Tom: {abonentExists1}");
var abonentExists2 = phoneBook.ContainsValue("Bob"); // false
Console.WriteLine($"Bob: {abonentExists2}");
phoneBook.Remove("+123456"); // удаление элемента
// проверяем количество элементов после удаления
Console.WriteLine($"Count: {phoneBook.Count}"); // Count: 0

413.

Строки и класс String
string s1 = "hello";
string s2 = new String('a', 6); // результатом будет строка "aaaaaa"
string s3 = new String(new char[] { 'w', 'o', 'r', 'l', 'd' });
string s4 = new String(new char[] { 'w', 'o', 'r', 'l', 'd' }, 1, 3); // orl
Console.WriteLine(s1); // hello
Console.WriteLine(s2); // aaaaaaa
Console.WriteLine(s3); // world
Console.WriteLine(s4); // orl

414.

Строка как набор символов
public char this[int index] {get;}
Применяя индексатор, мы можем обратиться к строке как к
массиву символов и получить по индексу любой из ее символов:
string message = "hello";
// получаем символ
char firstChar = message[1]; // символ 'e'
Console.WriteLine(firstChar); //e
Console.WriteLine(message.Length); // длина строки

415.

Перебор строк
string message = "hello";
for(var i =0; i < message.Length; i++)
{
Console.WriteLine(message[i]);
}
foreach(var ch in message)
{
Console.WriteLine(ch);
}

416.

Сравнение строк
string message1 = "hello";
string message2 = "hello";
Console.WriteLine(message1 == message2); // true

417.

Основные методы строк
• Compare: сравнивает две строки с учетом текущей культуры
(локали) пользователя
• CompareOrdinal: сравнивает две строки без учета локали
• Contains: определяет, содержится ли подстрока в строке
• Concat: соединяет строки
• CopyTo: копирует часть строки, начиная с определенного индекса
в массив
• EndsWith: определяет, совпадает ли конец строки с подстрокой
• Format: форматирует строку
• IndexOf: находит индекс первого вхождения символа или
подстроки в строке

418.

• Insert: вставляет в строку подстроку
• Join: соединяет элементы массива строк
• LastIndexOf: находит индекс последнего вхождения символа или
подстроки в строке
• Replace: замещает в строке символ или подстроку другим
символом или подстрокой
• Split: разделяет одну строку на массив строк
• Substring: извлекает из строки подстроку, начиная с указанной
позиции
• ToLower: переводит все символы строки в нижний регистр
• ToUpper: переводит все символы строки в верхний регистр
• Trim: удаляет начальные и конечные пробелы из строки

419.

Операции со строками
Объединение строк
Конкатенация строк или объединение может производиться как с
помощью операции +, так и с помощью метода Concat:
string s1 = "hello";
string s2 = "world";
string s3 = s1 + " " + s2; // результат: строка "hello world"
string s4 = string.Concat(s3, "!!!"); // результат: строка "hello world!!!"
Console.WriteLine(s4);

420.

Для объединения строк также может использоваться метод Join:
string s5 = "apple";
string s6 = "a day";
string s7 = "keeps";
string s8 = "a doctor";
string s9 = "away";
string[] values = new string[] { s5, s6, s7, s8, s9 };
string s10 = string.Join(" ", values);
Console.WriteLine(s10); // apple a day keeps a doctor away

421.

Сравнение строк
Для сравнения строк применяется статический метод Compare:
string s1 = "hello";
string s2 = "world";
int result = string.Compare(s1, s2);
if (result<0) {
Console.WriteLine("Строка s1 перед строкой s2");
}
else if (result > 0) {
Console.WriteLine("Строка s1 стоит после строки s2");
}
Else {
Console.WriteLine("Строки s1 и s2 идентичны");
}
// результатом будет "Строка s1 перед строкой s2"

422.

Поиск в строке
С помощью метода IndexOf мы можем определить индекс первого
вхождения отдельного символа или подстроки в строке:
string s1 = "hello world";
char ch = 'o';
int indexOfChar = s1.IndexOf(ch); // равно 4
Console.WriteLine(indexOfChar);
string substring = "wor";
int indexOfSubstring = s1.IndexOf(substring); // равно 6
Console.WriteLine(indexOfSubstring);

423.

Методы StartsWith и EndsWith
var files = new string[]
{
"myapp.exe",
"forest.jpg",
"main.exe",
"book.pdf",
"river.png"
};
for (int i = 0; i < files.Length; i++)
{
if (files[i].EndsWith(".exe"))
Console.WriteLine(files[i]);
}

424.

Разделение строк
string text = "И поэтому все так произошло";
string[] words = text.Split(new char[] { ' ' });
foreach (string s in words)
{
Console.WriteLine(s);
}
string[] words = text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

425.

Обрезка строки
Для обрезки начальных или концевых символов используется
функция Trim:
string text = " hello world ";
text = text.Trim(); // результат "hello world"
text = text.Trim(new char[] { 'd', 'h' }); // результат "ello worl«
Эта функция имеет частичные аналоги:
функция TrimStart обрезает начальные символы, а
функция TrimEnd обрезает конечные символы.

426.

Обрезать определенную часть строки позволяет функция Substring:
string text = "Хороший день";
// обрезаем начиная с третьего символа
text = text.Substring(2);
// результат "роший день"
Console.WriteLine(text);
// обрезаем сначала до последних двух символов
text = text.Substring(0, text.Length - 2);
// результат "роший де"
Console.WriteLine(text);

427.

Вставка
Для вставки одной строки в другую применяется
функция Insert:
string text = "Хороший день";
string substring = "замечательный ";
text = text.Insert(8, substring);
Console.WriteLine(text); // Хороший замечательный день

428.

Удаление строк
Удалить часть строки помогает метод Remove:
string text = "Хороший день";
// индекс последнего символа
int ind = text.Length - 1;
// вырезаем последний символ
text = text.Remove(ind);
Console.WriteLine(text); // Хороший ден
// вырезаем первые два символа
text = text.Remove(0, 2);
Console.WriteLine(text); // роший ден

429.

Замена
Чтобы заменить один символ или подстроку на другую,
применяется метод Replace:
string text = "хороший день";
text = text.Replace("хороший", "плохой");
Console.WriteLine(text); // плохой день
text = text.Replace("о", "");
Console.WriteLine(text); // плхй день

430.

Смена регистра
Для приведения строки к верхнему и нижнему
регистру используются соответственно
функции ToUpper() и ToLower():
string hello = "Hello world!";
Console.WriteLine(hello.ToLower()); // hello world!
Console.WriteLine(hello.ToUpper()); // HELLO WORLD!

431.

Форматирование строк
string name = "Tom";
int age = 23;
Console.WriteLine("Имя: {0} Возраст: {1}", name, age);
// консольный вывод
// Имя: Tom Возраст: 23

432.

string.Format
string name = "Tom";
int age = 23;
string output = string.Format("Имя: {0} Возраст:
{1}", name, age);
Console.WriteLine(output);

433.

Спецификаторы форматирования
C/c
Задает формат денежной единицы, указывает количество десятичных
разрядов после запятой
D / d Целочисленный формат, указывает минимальное количество цифр
E/e
F/f
Экспоненциальное представление числа, указывает количество
десятичных разрядов после запятой
Формат дробных чисел с фиксированной точкой, указывает количество
десятичных разрядов после запятой
G / g Задает более короткий из двух форматов: F или E
N / n Также задает формат дробных чисел с фиксированной точкой,
определяет количество разрядов после запятой
P/p
Задает отображения знака процентов рядом с число, указывает
количество десятичных разрядов после запятой
X/x
Шестнадцатеричный формат числа

434.

Форматирование валюты
double number = 23.7;
string result = string.Format("{0:C0}", number);
Console.WriteLine(result); // 24 р.
string result2 = string.Format("{0:C2}", number);
Console.WriteLine(result2); // 23,70 р.

435.

Форматирование целых чисел
int number = 23;
string result = string.Format("{0:d}", number);
Console.WriteLine(result); // 23
string result2 = string.Format("{0:d4}", number);
Console.WriteLine(result2); // 0023

436.

Форматирование дробных чисел
int number = 23;
string result = string.Format("{0:f}", number);
Console.WriteLine(result); // 23,00
double number2 = 45.08;
string result2 = string.Format("{0:f4}", number2);
Console.WriteLine(result2); // 45,0800
double number3 = 25.07;
string result3 = string.Format("{0:f1}", number3);
Console.WriteLine(result3); // 25,1

437.

Формат процентов
decimal number = 0.15345m;
Console.WriteLine("{0:P1}", number);// 15,3%

438.

Настраиваемые форматы
Используя знак #, можно настроить формат вывода.
Например, нам надо вывести некоторое число в формате
телефона +х (ххх)ххх-хх-хх:
long number = 19876543210;
string result = string.Format("{0:+# (###) ###-##-##}",
number);
Console.WriteLine(result); // +1 (987) 654-32-10

439.

Метод ToString
long number = 19876543210;
Console.WriteLine(number.ToString("+# (###) ###-##-##"));
// +1 (987) 654-32-10
double money = 24.8;
Console.WriteLine(money.ToString("C2")); // 24,80 р.

440.

Интерполяция строк
string name = "Tom";
int age = 23;
Console.WriteLine($"Имя: {name} Возраст: {age}");
// консольный вывод
// Имя: Tom Возраст: 23

441.

int x = 8;
int y = 7;
string result = $"{x} + {y} = {x + y}";
Console.WriteLine(result); // 8 + 7 = 15
___________________________________________
int x = 8;
int y = 7;
string result = $"{x} * {y} = {Multiply(x, y)}";
Console.WriteLine(result); // 8 * 7 = 56
int Multiply(int a, int b) => a * b;

442.

long number = 19876543210;
Console.WriteLine($"{number:+# ### ### ## ##}"); // +1 987 654
32 10
__________________________________________________
string name = "Tom";
int age = 23;
Console.WriteLine($"Имя: {name, -5} Возраст: {age}"); //
пробелы после
Console.WriteLine($"Имя: {name, 5} Возраст: {age}"); //
пробелы до
Имя: Том Возраст: 23
Имя: Том Возраст: 23

443.

Создание StringBuilder
using System.Text;
StringBuilder sb = new StringBuilder();
Можно сразу инициализировать объект определенной строкой:
StringBuilder sb = new StringBuilder("Привет мир");
С помощью метода ToString() мы можем получить строку, которая хранится в
StringBuilder:
var sb = new StringBuilder("Hello World");
Console.WriteLine(sb.ToString()); // Hello World
Либо можно просто передать объект StringBuilder:
var sb = new StringBuilder("Hello World");
Console.WriteLine(sb); // Hello World

444.

Длина и емкость StringBuilder
using System.Text;
StringBuilder sb = new StringBuilder("Привет мир");
Console.WriteLine($"Длина: {sb.Length}");
// Длина: 10
Console.WriteLine($"Емкость: {sb.Capacity}"); // Емкость: 16
var sb = new StringBuilder(32);
StringBuilder также позволяет сразу задать строку и емкость:
var sb = new StringBuilder("Привет мир", 32);

445.

Операции со строками в StringBuilder
• Append: добавляет подстроку в объект StringBuilder
• Insert: вставляет подстроку в объект StringBuilder,
начиная с определенного индекса
• Remove: удаляет определенное количество символов,
начиная с определенного индекса
• Replace: заменяет все вхождения определенного
символа или подстроки на другой символ или подстроку
• AppendFormat: добавляет подстроку в конец объекта
StringBuilder

446.

using System.Text;
var sb = new StringBuilder("Название: ");
Console.WriteLine(sb); // Название:
Console.WriteLine($"Длина: {sb.Length}"); // 10
Console.WriteLine($"Емкость: {sb.Capacity}"); // 16
sb.Append(" Руководство");
Console.WriteLine(sb); // Название: Руководство
Console.WriteLine($"Длина: {sb.Length}"); // 22
Console.WriteLine($"Емкость: {sb.Capacity}"); // 32
sb.Append(" по C#");
Console.WriteLine(sb); // Название: Руководство по C#
Console.WriteLine($"Длина: {sb.Length}"); // 28
Console.WriteLine($"Емкость: {sb.Capacity}"); // 32

447.

var sb = new StringBuilder("Привет мир");
sb.Append("!");
sb.Insert(7, "компьютерный ");
Console.WriteLine(sb); // Привет компьютерный мир!
// заменяем слово
sb.Replace("мир", "world");
Console.WriteLine(sb); // Привет компьютерный world!
// удаляем 13 символов, начиная с 7-го
sb.Remove(7, 13);
Console.WriteLine(sb); // Привет world!
// получаем строку из объекта StringBuilder
string text = sb.ToString();
Console.WriteLine(text); // Привет world!

448.

Когда надо использовать класс String, а когда StringBuilder?
Microsoft рекомендует использовать класс String в следующих случаях:
• При небольшом количестве операций и изменений над строками
• При выполнении фиксированного количества операций объединения.
В этом случае компилятор может объединить все операции
объединения в одну
• Когда надо выполнять масштабные операции поиска при построении
строки, например IndexOf или StartsWith. Класс StringBuilder не имеет
подобных методов.
Класс StringBuilder рекомендуется использовать в следующих случаях:
• При неизвестном количестве операций и изменений над строками во
время выполнения программы
• Когда предполагается, что приложению придется сделать множество
подобных операций

449.

Регулярные выражения
using System.Text.RegularExpressions;
string s = "Бык тупогуб, тупогубенький бычок, у быка губа бела была тупа";
Regex regex = new Regex(@"туп(\w*)");
MatchCollection matches = regex.Matches(s);
if (matches.Count > 0)
{
тупогуб
foreach (Match match in matches)
Console.WriteLine(match.Value);
тупогубенький
}
тупа
else
{
Console.WriteLine("Совпадений не найдено");
}

450.

Параметр RegexOptions
• Compiled: при установке этого значения регулярное выражение
компилируется в сборку, что обеспечивает более быстрое выполнение
• CultureInvariant: при установке этого значения будут игнорироваться
региональные различия
• IgnoreCase: при установке этого значения будет игнорироваться
регистр
• IgnorePatternWhitespace: удаляет из строки пробелы и разрешает
комментарии, начинающиеся со знака #
• Multiline: указывает, что текст надо рассматривать в многострочном
режиме. При таком режиме символы "^" и "$" совпадают,
соответственно, с началом и концом любой строки, а не с началом и
концом всего текста
• RightToLeft: приписывает читать строку справа налево
• Singleline: при данном режиме символ "." соответствует любому
символу, в том числе последовательности "\n", которая осуществляет
переход на следующую строку

451.

Например:
Regex regex = new Regex(@"туп(\w*)", RegexOptions.IgnoreCase);
При необходимости можно установить несколько параметров:
Regex regex = new Regex(@"туп(\w*)", RegexOptions.Compiled |
RegexOptions.IgnoreCase);

452.

Синтаксис регулярных выражений
^: соответствие должно начинаться в начале строки
(например, выражение @"^пр\w*" соответствует слову
"привет" в строке "привет мир")
$: конец строки (например,
выражение @"\w*ир$" соответствует слову "мир" в строке
"привет мир", так как часть "ир" находится в самом конце)
.: знак точки определяет любой одиночный символ
(например, выражение "м.р" соответствует слову "мир"
или "мор")
*: предыдущий символ повторяется 0 и более раз
+: предыдущий символ повторяется 1 и более раз

453.

?: предыдущий символ повторяется 0 или 1 раз
\s: соответствует любому пробельному символу
\S: соответствует любому символу, не являющемуся
пробелом
\w: соответствует любому алфавитно-цифровому символу
\W: соответствует любому не алфавитно-цифровому
символу
\d: соответствует любой десятичной цифре
\D : соответствует любому символу, не являющемуся
десятичной цифрой

454.

string s = "Бык тупогуб, тупогубенький бычок, у быка губа
бела была тупа";
Regex regex = new Regex(@"\w*губ\w*");
__________________________________________________
string s = "456-435-2318";
Regex regex = new Regex(@"\d{3}-\d{3}-\d{4}");
__________________________________________________
string s = "456-435-2318";
Regex regex = new Regex("[0-9]{3}-[0-9]{3}-[0-9]{4}");

455.

Проверка на соответствие строки формату
using System.Text.RegularExpressions;
string pattern = @"^(?("")(""[^""]+?""@)|(([0-9a-z]((\.(?!\.))|[!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" + @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([09a-z][-\w]*[0-9a-z]*\.)+[a-z0-9]{2,17}))$";
var data = new string[] {
"tom@gmail.com",
"+12345678999",
"sam@yandex.ru",
"+43743989393"
};
Console.WriteLine("Email List");
for(int i = 0; i < data.Length; i++) {
if (Regex.IsMatch(data[i], pattern, RegexOptions.IgnoreCase)) {
Console.WriteLine(data[i]);
}
}

456.

Замена и метод Replace
string text = "Мама мыла раму. ";
string pattern = @"\s+";
string target = " ";
Regex regex = new Regex(pattern);
string result = regex.Replace(text, target);
Console.WriteLine(result);

457.

string phoneNumber = "+1(876)-234-12-98";
string pattern = @"\D";
string target = "";
Regex regex = new Regex(pattern);
string result = regex.Replace(phoneNumber, target);
Console.WriteLine(result); // 18762341298

458.

Работа с датами и временем
Структура DateTime
DateTime dateTime = new DateTime();
Console.WriteLine(dateTime); // 01.01.0001 0:00:00
Чтобы задать конкретную дату, нужно использовать один из
конструкторов, принимающих параметры:
DateTime date1 = new DateTime(2015, 7, 20); // год - месяц - день
Console.WriteLine(date1); // 20.07.2015 0:00:00
Установка времени:
DateTime date1 = new DateTime(2015, 7, 20, 18, 30, 25); // год - месяц день - час - минута - секунда
Console.WriteLine(date1); // 20.07.2015 18:30:25

459.

Console.WriteLine(DateTime.Now); - берет
текущую дату и время компьютера
Console.WriteLine(DateTime.UtcNow); - дата и
время относительно времени по Гринвичу
(GMT)
Console.WriteLine(DateTime.Today); - только
текущая дата

460.

Операции с DateTime
Add(TimeSpan value): добавляет к дате значение TimeSpan
AddDays(double value): добавляет к текущей дате несколько
дней
AddHours(double value): добавляет к текущей дате несколько
часов
AddMinutes(double value): добавляет к текущей дате
несколько минут
AddMonths(int value): добавляет к текущей дате несколько
месяцев
AddYears(int value): добавляет к текущей дате несколько лет

461.

Для вычитания дат используется метод Subtract(DateTime
date):
DateTime date1 = new DateTime(2015, 7, 20, 18, 30, 25); //
20.07.2015 18:30:25
DateTime date2 = new DateTime(2015, 7, 20, 15, 30, 25); //
20.07.2015 15:30:25
Console.WriteLine(date1.Subtract(date2)); // 03:00:00

462.

DateTime date1 = new DateTime(2015, 7, 20, 18, 30, 25);
Console.WriteLine(date1.ToLocalTime()); // 20.07.2015
21:30:25
Console.WriteLine(date1.ToUniversalTime()); // 20.07.2015
15:30:25
Console.WriteLine(date1.ToLongDateString()); // 20 июля
2015 г.
Console.WriteLine(date1.ToShortDateString()); // 20.07.2015
Console.WriteLine(date1.ToLongTimeString()); // 18:30:25
Console.WriteLine(date1.ToShortTimeString()); // 18:30

463.

Форматирование дат и времени
Описатель Описание
D
Полный формат даты. Например, 17 июля 2015 г.
d
Краткий формат даты. Например, 17.07.2015
F
Полный формат даты и времени. Например, 17 июля
2015 г. 17:04:43
f
Полный формат даты и краткий формат времени.
Например, 17 июля 2015 г. 17:04
G
Краткий формат даты и полный формат времени.
Например, 17.07.2015 17:04:43
g
Краткий формат даты и времени. Например, 17.07.2015
17:04
M, m
Формат дней месяца. Например, 17 июля

464.

O, o Формат обратного преобразования даты и времени. Вывод
даты и времени в соответствии со стандартом ISO 8601 в
формате "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffzzz". Например,
2015-07-17T17:04:43.4092892+03:00
R, r Время по Гринвичу. Например, Fri, 17 Jul 2015 17:04:43 GMT
s
Сортируемый формат даты и времени. Например, 2015-0717T17:04:43
T
Полный формат времени. Например, 17:04:43
t
Краткий формат времени. Например, 17:04
U
Полный универсальный полный формат даты и времени.
Например, 17 июля 2015 г. 17:04:43
u
Краткий универсальный полный формат даты и времени.
Например, 2015-07-17 17:04:43Z
Y, y Формат года. Например, Июль 2015

465.

DateTime now = DateTime.Now;
Console.WriteLine($"D: {now.ToString("D")}");
Console.WriteLine($"d: {now.ToString("d")}");
Console.WriteLine($"F: {now.ToString("F")}");
Console.WriteLine($"f: {now:f}");
Console.WriteLine($"G: {now:G}");
Console.WriteLine($"g: {now:g}");
Console.WriteLine($"M: {now:M}");
Console.WriteLine($"O: {now:O}");
Console.WriteLine($"o: {now:o}");
Console.WriteLine($"R: {now:R}");
Console.WriteLine($"s: {now:s}");
Console.WriteLine($"T: {now:T}");
Console.WriteLine($"t: {now:t}");
Console.WriteLine($"U: {now:U}");
Console.WriteLine($"u: {now:u}");
Console.WriteLine($"Y: {now:Y}");
D: 6 января 2022 г.
d: 06.01.2022
F: 6 января 2022 г. 14:45:20
f: 6 января 2022 г. 14:45
G: 06.01.2022 14:45:20
g: 06.01.2022 14:45
M: 6 января
O: 2022-01-06T14:45:20.3942344+04:00
o: 2022-01-06T14:45:20.3942344+04:00
R: Thu, 06 Jan 2022 14:45:20 GMT
s: 2022-01-06T14:45:20
T: 14:45:20
t: 14:45
U: 6 января 2022 г. 10:45:20
u: 2022-01-06 14:45:20Z
Y: январь 2022 г.

466.

Настройка формата времени и даты
Описатель Описание
d
Представляет день месяца от 1 до 31. Одноразрядные числа
используются без нуля в начале
dd
Представляет день месяца от 1 до 31. К одноразрядным числам в
начале добавляется ноль
ddd
Сокращенное название дня недели
dddd
Полное название дня недели
f / fffffff
Представляет миллисекунды. Количество символов f указывает на
число разрядов в миллисекундах
g
Представляет период или эру (например, "н. э.")
h
Часы в виде от 1 до 12. Часы с одной цифрой не дополняются нулем
hh
Часы в виде от 01 до 12. Часы с одной цифрой дополняются нулем
H
Часы в виде от 0 до 23. Часы с одной цифрой не дополняются нулем
HH
Часы в виде от 0 до 23. Часы с одной цифрой дополняются нулем

467.

K
m
Часовой пояс
Минуты от 0 до 59. Минуты с одной цифрой не дополняются
начальным нулем
mm
Минуты от 0 до 59. Минуты с одной цифрой дополняются начальным
нулем
M
Месяц в виде от 1 до 12
MM
Месяц в виде от 1 до 12. Месяц с одной цифрой дополняется
начальным нулем
MMM
Сокращенное название месяца
MMMM Полное название месяца
s
Секунды в виде числа от 0 до 59. Секунды с одной цифрой не
дополняются начальным нулем
ss
Секунды в виде числа от 0 до 59. Секунды с одной цифрой
дополняются начальным нулем
t
Первые символы в обозначениях AM и PM
tt
AM или PM

468.

y
yy
yyy
yyyy
yyyyy
z
zz
Представляет год как число из одной или двух цифр. Если год имеет
более двух цифр, то в результате отображаются только две младшие
цифры
Представляет год как число из одной или двух цифр. Если год имеет
более двух цифр, то в результате отображаются только две младшие
цифры. Если год имеет одну цифру, то он дополняется начальным
нулем
Год из трех цифр
Год из четырех цифр
Год из пяти цифр. Если в году меньше пяти цифр, то он дополняется
начальными нулями
Представляет смещение в часах относительно времени UTC
Представляет смещение в часах относительно времени UTC. Если
смещение представляет одну цифру, то она дополняется начальным
нулем.

469.

DateOnly
DateOnly()
DateOnly(int year, int month, int day)
DateOnly(int year, int month, int day, System.Globalization.Calendar
calendar)
______________________________________________________
DateOnly someDate = new DateOnly();
Console.WriteLine(someDate); // 01.01.0001
DateOnly someDate = new DateOnly(2022,1,6); // 6 января 2022 года
Console.WriteLine(someDate); // 06.01.2022

470.

Свойства DateOnly
• Day: возвращает день даты
• DayNumber: возвращает количество прошедших дней с
1 января 0001 года относительно григорианского
календаря
• DayOfWeek: возвращает день недели
• DayOfYear: возвращает день года
• MaxValue: возвращает максимально возможную дату
(статическое свойство)
• MinValue: возвращает самую раннюю возможную дату
(статическое свойство)
• Month: возвращает месяц
• Year: возвращает год

471.

Применение свойств:
DateOnly now = new DateOnly(2022,1,6);
Console.WriteLine(now.Day);
// 6
Console.WriteLine(now.DayNumber); // 738160
Console.WriteLine(now.DayOfWeek); // Thursday
Console.WriteLine(now.DayOfYear); // 6
Console.WriteLine(now.Month);
// 1
Console.WriteLine(now.Year);
// 2022

472.

Методы DateOnly
AddDays(int days): добавляет к дате некоторое количество дней
AddMonths(int months): добавляет к дате некоторое количество
месяцев
AddYears(int years): добавляет к дате некоторое количество лет
ToDateTime(TimeOnly): возвращает объект DateTime, который в
качестве даты исппользует текущий объект DateOnly, а в качестве
времени - значение параметра в виде TimeOnly
ToLongDateString(): выводит текущий объект DateOnly в виде
подробной даты
ToShortDateString(): выводит текущий объект DateOnly в виде
сжатой даты

473.

FromDateTime(DateTime dateTime): на основе значения DateTime,
переданного через параметр, создает и возвращает объект DateOnly
FromDayNumber(int days): на основе количества дней создает и
возвращает объект DateOnly
Parse(string date): конвертирует строковое представление даты в объект
DateOnly
ParseExact(string date, string format): конвертирует строковое
представление даты в объект DateOnly, применяя определенный
формат
TryParse(String, DateOnly): конвертирует строковое представление даты
в объект DateOnly. При успешной конвертации возвращает true, а
параметр типа DateOnly содержит созданную дату
TryParseExact(String, String, DateOnly): конвертирует строковое
представление даты в объект DateOnly, применяя определенный
формат. При успешной конвертации возвращает true, а параметр типа
DateOnly содержит созданную дату

474.

Пример некоторых операций:
DateOnly now = DateOnly.Parse("06.01.2022"); // на
русскоязычной локализованной ОС
Console.WriteLine(now); // 06.01.2022
now = now.AddDays(1); // 07.01.2022
now = now.AddMonths(4); // 07.05.2022
now = now.AddYears(-1); // 07.05.2021
Console.WriteLine(now.ToShortDateString()); // 07.05.2021
Console.WriteLine(now.ToLongDateString()); // 7 мая 2021 г.

475.

TimeOnly
TimeOnly()
TimeOnly(long ticks)
TimeOnly(int hour, int minute)
TimeOnly(int hour, int minute, int second)
TimeOnly(int hour, int minute, int second, int
millisecond)

476.

TimeOnly time = new TimeOnly();
Console.WriteLine(time); // 0:00
TimeOnly time1 = new TimeOnly(4, 30);
Console.WriteLine(time1); // 4: 30
TimeOnly time2 = new TimeOnly(14, 23, 30);
Console.WriteLine(time2); // 14: 23

477.

Свойства TimeOnly
• Hour: возвращает количество часов
• Minute: возвращает количество минут
• Second: возвращает количество секунд
• Millisecond: возвращает количество миллисекунд
• Ticks: возвращает количество тиков
• MaxValue: возвращает максимально возможное
время (статическое свойство)
• MinValue: возвращает минимально возможное время
(статическое свойство)

478.

Применение свойств:
TimeOnly time = new TimeOnly(14, 23, 30);
Console.WriteLine(time.Hour);
// 14
Console.WriteLine(time.Minute); // 23
Console.WriteLine(time.Second); // 30

479.

Методы TimeOnly
AddHours(double hours): добавляет к времени некоторое
количество часов
AddMinutes(double minutes): добавляет к времени некоторое
количество минут
Add(TimeSpan value): добавляет время из объекта TimeSpan
ToDateTime(DateOnly): возвращает объект DateTime, который в
качестве времени использует текущий объект TimeOnly, а в
качестве даты - значение параметра в виде DateOnly
ToLongTimeString(): выводит текущий объект TimeOnly в виде
подробного времени
ToShortTimeString(): выводит текущий объект TimeOnly в виде
сжатого времени

480.

FromDateTime(DateTime dateTime): на основе значения DateTime,
переданного через параметр, создает и возвращает объект TimeOnly
FromTimeSpan(TimeSpan value): на основе объекта TimeSpan создает и
возвращает объект TimeOnly
Parse(string time): конвертирует строковое представление времени в
объект TimeOnly
ParseExact(string timee, string format): конвертирует строковое
представление времени в объект TimeOnly, применяя определенный
формат
TryParse(string time, TimeOnly result): конвертирует строковое
представление времени в объект TimeOnly. При успешной конвертации
возвращает true, а параметр типа TimeOnly содержит
сконвертированное время
TryParseExact(string time, string format, TimeOnly result): конвертирует
строковое представление времени в объект TimeOnly, применяя
определенный формат. При успешной конвертации возвращает true, а
параметр типа TimeOnly содержит сконвертированное время

481.

Пример некоторых операций:
TimeOnly time = TimeOnly.Parse("06:33:22");
Console.WriteLine(time);
// 6:33
time = time.AddHours(1);
// 7:33
time = time.AddMinutes(-23); // 7:10
Console.WriteLine(time.ToShortTimeString());// 7:10
Console.WriteLine(time.ToLongTimeString()); // 7:10:22
English     Русский Rules