Нет описания

vivanov 88f3d69b87 first commit 10 месяцев назад
.gitignore.txt 429e5ab52f first commit 11 месяцев назад
readme.md 88f3d69b87 first commit 10 месяцев назад

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