No Description

vivanov 88f3d69b87 first commit 10 months ago
.gitignore.txt 429e5ab52f first commit 11 months ago
readme.md 88f3d69b87 first commit 10 months ago

readme.md

Конспект лекции "Исключения"

Конструкция try..catch..finally, простыми словами - это замена if и else. Она подходит для записи больших условий при том условии, что код не будет выглядеть большим.

try
{
     
}
catch
{
     
}
finally
{
     
}

1)

Код выглядит следующим образом:

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

Код выводит следующую ошибку из-за неверного математического вычисления:

Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.                  
   at Program.<Main>$(String[] args) in C:\Users\шоумен\RiderProjects\lab8\lab8\Program.cs:line 2

Process finished with exit code -1,073,741,676.

Код выглядит следующим образом:

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

Из-за неверного деления программа выведет следующее:

Возникло исключение!
Блок finally   
Конец программы

Код выглядит следующим образом:

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");
}

Результат кода таков:

Блок finally

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

Console.WriteLine("Введите число");
int x;
string input = Console.ReadLine();
if (Int32.TryParse(input, out x))
{
    x *= x;
    Console.WriteLine("Квадрат числа: " + x);
}
else
{
    Console.WriteLine("Некорректный ввод");
}
Console.Read();

По другому код можно записать таким образом:

Console.WriteLine("Введите число");
int x = Int32.Parse(Console.ReadLine());

x *= x;
Console.WriteLine("Квадрат числа: " + x);
Console.Read();

У блока catch есть 2 формы. Одна из них выглядит таким образом:

catch
{
    // выполняемые инструкции
}

Оно обрабатывает любое исключение, которое появляется в блоке try.

Форая форма выглядит немного иначе:

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

Это блок обрабатывает только то исключение, которое соответсвтует типу, указанному в скобках. Так же если в блоке try будет другое исключение то оно не будет обработано.

Пример такой программы:

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

Так же есть еще один вариант, где обрабатывается только то исключение, которое соответствует типу указанных в скобках:

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

Пример такой программы:

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

Фильтры исключений

Фильтры, в которых после блока идет вырвжение с условием в свкобках.

catch when(условие)
{
     
}

Пример такой программы, которая выполняет исключение, если условие в выражении истинно:

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

Типы исключений. Класс Exception

Базовый тип исключения - это тип Exception. С помощью этого типа можно получить информацию об исключении.

InnerException: хранит информацию об исключении, которое послужило причиной текущего исключения

Message: хранит сообщение об исключении, текст ошибки

Source: хранит имя объекта или сборки, которое вызвало исключение

StackTrace: возвращает строковое представление стека вызывов, которые привели к возникновению исключения

TargetSite: возвращает метод, в котором и было вызвано исключение

Пример обработки типа Exception.

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}");
}

Console.Read();

Код выводит такой текст:

Исключение: Attempted to divide by zero.
Метод: Void <Main>$(System.String[])                                                                                
Трассировка стека:    at Program.<Main>$(String[] args) in C:\Users\шоумен\RiderProjects\lab8\lab8\Program.cs:line 4

Из-за того, что тип Exception является базовым типом для всех исключений, то выражение catch (Exception ex) будет обрабатывать все исключения, которые могут возникнуть:

Так же есть специализированные типы исключений, которые нужны для обработки определнных типов исключений:

DivideByZeroException: представляет исключение, которое генерируется при делении на ноль

ArgumentOutOfRangeException: генерируется, если значение аргумента находится вне диапазона допустимых значений

ArgumentException: генерируется, если в метод для параметра передается некорректное значение

IndexOutOfRangeException: генерируется, если индекс элемента массива или коллекции находится вне диапазона допустимых значений

InvalidCastException: генерируется при попытке произвести недопустимые преобразования типов

NullReferenceException: генерируется при попытке обращения к объекту, который равен null (то есть по сути неопределен)

Еще мы можем выключить дополительные блоки catch, это как else if.

Пример кода:

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();

В данном случае блоки catch обрабатывают исключения типов IndexOutOfRangeException и DivideByZeroException. Когда в блоке try возникнет исключение, то CLR будет искать нужный блок catch для обработки исключения. Так, в данном случае на строке

numbers[7] = 9;

происходит обращение к 7-му элементу массива. Однако поскольку в массиве только 4 элемента, то мы получим исключение типа IndexOutOfRangeException. CLR найдет блок catch, который обрабатывает данное исключение, и передаст ему управление.

Есть другая ситуация:

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

В данном случае в блоке try генерируется исключение типа InvalidCastException, но такого блока catch не существует, поэтому программа принудительно завершится.

Мы также можем определить для InvalidCastException свой блок catch, однако суть в том, что теоретически в коде могут быть сгенерированы сами различные типы исключений. А определять для всех типов исключений блоки catch, если обработка исключений однотипна, не имеет смысла. И в этом случае мы можем определить блок catch для базового типа Exception:

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

Создание классов исключений

Ограничение по возрасту

try
{
    Person p = new Person { Name = "Tom", Age = 17 };
}
catch (Exception ex)
{
    Console.WriteLine($"Ошибка: {ex.Message}");
}
Console.Read();

class Person
{
    private int age;
    public string Name { get; set; }
    public int Age
    {
        get { return age; }
        set
        {
            if (value < 18)
            {
                throw new Exception("Лицам до 18 регистрация запрещена");
            }
            else
            {
                age = value;
            }
        }
    }
}
Ошибка: Лицам до 18 регистрация запрещена

Поиск блока catch при обработке исключений

try
{
    TestClass.Method1();
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"Catch в Main : {ex.Message}");
}
finally
{
    Console.WriteLine("Блок finally в Main");
}
Console.WriteLine("Конец метода Main");
Console.Read();

class TestClass
{
    public static void Method1()
    {
        try
        {
            Method2();
        }
        catch (IndexOutOfRangeException ex)
        {
            Console.WriteLine($"Catch в Method1 : {ex.Message}");
        }
        finally
        {
            Console.WriteLine("Блок finally в Method1");
        }
        Console.WriteLine("Конец метода Method1");
    }
    static void Method2()
    {
        try
        {
            int x = 8;
            int y = x / 0;
        }
        finally
        {
            Console.WriteLine("Блок finally в Method2");
        }
        Console.WriteLine("Конец метода Method2");
    }
}
Блок finally в Method2
Блок finally в Method1                     
Catch в Main : Attempted to divide by zero.
Блок finally в Main                        
Конец метода Main   

Генерация исключения и оператор throw

try
{
    Console.Write("Введите строку: ");
    string message = Console.ReadLine();
    if (message.Length > 6)
    {
        throw new Exception(
            "Длина строки больше 6 символов");
    }
}
catch (Exception e)
{
    Console.WriteLine($"Ошибка: {e.Message}");
}
Console.Read();
Введите строку: 1234567
Ошибка: Длина строки больше 6 символов

Проверка экземпляра типа значения, допускающего значение NULL

int? a = 42;
if (a is int valueOfA)
{
    Console.WriteLine($"a is {valueOfA}");
}
else
{
    Console.WriteLine("a does not have a value");
}
a is 42
int? a = 28;
int b = a ?? -1;
Console.WriteLine($"b is {b}");  // output: b is 28

int? c = null;
int d = c ?? -1;
Console.WriteLine($"d is {d}");  // output: d is -1

Операторы с нулификацией

int? a = 10;
int? b = null;
int? c = 10;

a++;        // a is 11
a = a * c;  // a is 110
a = a + b;  // a is null
int? a = 10;
Console.WriteLine($"{a} >= null is {a >= null}");
Console.WriteLine($"{a} < null is {a < null}");
Console.WriteLine($"{a} == null is {a == null}");
// Output:
// 10 >= null is False
// 10 < null is False
// 10 == null is False

int? b = null;
int? c = null;
Console.WriteLine($"null >= null is {b >= c}");
Console.WriteLine($"null == null is {b == c}");
// Output:
// null >= null is False
// null == null is True

Упаковка-преобразование и распаковка-преобразование

Если HasValue возвращает false, создается пустая ссылка. Если HasValue возвращает true, упаковывается соответствующее значение базового типа T, а не экземпляр Nullable.

int a = 41;
object aBoxed = a;
int? aNullable = (int?)aBoxed;
Console.WriteLine($"Value of aNullable: {aNullable}");

object aNullableBoxed = aNullable;
if (aNullableBoxed is int valueOfA)
{
    Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
}
// Output:
// Value of aNullable: 41
// aNullableBoxed is boxed int: 41

Оператор ??

object x = null;
object y = x ?? 100;  // равно 100, так как x равен null
 
object z = 200;
object t = z ?? 44; // равно 200, так как z не равен null

Оператор условного null

class User
{
    public Phone Phone { get; set; }
}
 
class Phone
{
    public Company Company { get; set; }
}
 
class Company
{
    public string Name { get; set; }
}

Объект User содержит ссылку на объект Phone, а объект Phone содержит ссылку на объект Company, поэтому теоретически мы можем получить из объекта User название компании, например, так:

User user = new User();
Console.WriteLine(user.Phone.Company.Name);