try catch c#

try catch c#

Всем доброго времени суток. На связи Алексей Гулынин. В прошлой статье мы познакомились с отладкой кода в Visual Studio. В данной статье я бы хотел рассказать про конструкцию try catch в C#. Приготовьтесь, информации будет много. В любой программе может возникнуть ситуация, при которой логика, которая заложена в неё, не выполнится. К примеру, вы пытаетесь разделить на ноль или пытаетесь обратиться к файлу или папке, которых не существует (или они недоступны).

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

Для обработки таких ситуаций в объектно-ориентированных языках придумали понятие "Исключение" или "Exception".

Как было до введения исключений: раньше были функции, которые в результате своей работы давали различные коды возврата. Допустим мы пытаемся открыть файл и прочитать его. Результат работы данной функции вернёт какое-нибудь число (это и есть код возврата). Обычно, если функция возвращает 0 — то всё нормально. Также эта функция может вернуть "-1" (например, файл не существует) или "-3" (к примеру, нет доступа к файлу), и таких чисел может быть много. И все эти числа нужно как-то анализировать, чтобы выдать конечному пользователю адекватную информацию об ошибке. Все эти числа обрабатывались либо в цикле, либо с помощью конструкции switch case (вариант предпочтительней). Чем больше в коде стандартных функций, тем больше будет этих switch case, и код будет сильно расти (из-за того, что ошибки нужно обрабатывать). Все эти коды ошибок были описаны в документации, и можно было бы конечно написать вот так "Код ошибки 404. Обратитесь к документации." и дать пользователю ссылку на документацию. Но это совсем неправильный подход.

Что же нам предлагает понятие Exception?

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

Давайте на простом примере рассмотрим как это всё записывается (так будет проще понять):

static void Main(string[] args)
{
	Console.WriteLine(DivideNumbers(10, 2));
	Console.ReadLine();
}

static int DivideNumbers(int i1, int i2)
{
	return i1 / i2; //выйдет 5
}

В данном случае программа честно выведет число 5. Давайте попробуем разделить на 0. Сейчас программа аварийно завершится и выйдет необработанное исключение. Причем среда нам уже подсказывает, что произошло деление на ноль:

try catch в C#

Теперь давайте обработаем это исключение в коде. Делается это с помощью конструкции try catch в C#:

static void Main(string[] args)
{
	try
	{
		Console.WriteLine(DivideNumbers(10, 0));
	}
	catch (DivideByZeroException ex)
	{
		Console.WriteLine("Произошло деление на ноль");
	}
	Console.ReadLine();
}
static int DivideNumbers(int i1, int i2)
{
	return i1 / i2;
}

Если теперь запустить программу, то мы увидим, что выйдет сообщение "Произошло деление на ноль". Наша программа поймала исключение и обработала его. На данный момент мы пока ловим исключения, связанные с делением на ноль. А что будет если сработает исключение другого класса. Давайте попробуем разделить не на 0, а на какую-нибудь строку, к примеру на "abc". Видоизменим для начала наш метод DivideNumbers:

static int DivideNumbers(int i1, string s2)
{
	int i2 = int.Parse(s2);
	return i1 / i2;
}

Если мы теперь запустим программу, то она завершится аварийно и выйдет другое исключение — FormatException. В данном случае мы хотим число разделить на строку, что делать нельзя. Давайте обработаем это исключение, заодно посмотрим, как обрабатывать несколько исключений подряд (обратите внимание на параметр ex):

static void Main(string[] args)
{
	try
	{
		Console.WriteLine(DivideNumbers(10, "abc"));
	}
	catch (DivideByZeroException ex)
	{
		Console.WriteLine("Произошло деление на ноль");
	}
	catch (FormatException ex)
	{
		Console.WriteLine("Нельзя делить число на строку");
		Console.WriteLine(ex.Message); //выйдет сообщение "Входная строка имела неверный формат"
	}
	Console.ReadLine();
}

Если мы сейчас запустим программу, то выйдет сообщение "Нельзя делить число на строку". Можно также пойти по пути наименьшего сопротивления и сделать проще: на все исключения поставить один обработчик (в серьёзных программах так не делается):

static void Main(string[] args)
{
	try
	{
		Console.WriteLine(DivideNumbers(10, "abc"));
	}
	catch
	{
		Console.WriteLine("Произошла какая-то ошибка. Обратитесь к администратору.");
	}
	Console.ReadLine();
}

Хочу ещё рассказать про один тонкий момент. Все классы исключений (DivideByZeroException, FormatException и другие) наследуют базовый класс Exception. Т.е. по сути мы можем написать, как в примере кода ниже, и будет отловлен DivideByZeroException:

static void Main(string[] args)
{
	try
	{
		Console.WriteLine(DivideNumbers(10, 0));
	}
	catch (Exception ex)
	{
		Console.WriteLine("Произошло деление на ноль");
	}
	Console.ReadLine();
}
static int DivideNumbers(int i1, int i2)
{
	return i1 / i2;
}

Когда мы отлавливаем в коде исключения, класс Exception будет ловить все классы наследников. Нужно указывать сначала дочерние классы, а потом родительские:

static void Main(string[] args)
{
   try
   {
      Console.WriteLine(DivideNumbers(10, "abc"));
   }
   catch (DivideByZeroException ex)
   {
      Console.WriteLine("Произошло деление на ноль");
   }
   catch (FormatException ex)
   {
      Console.WriteLine("Нельзя делить число на строку");
      Console.WriteLine(ex.Message); //выйдет сообщение "Входная строка имела неверный формат"
   }
   catch (Exception ex)
   {
      Console.WriteLine("Вышла какая-то ошибка. Обратитесь к администратору.");
   }
   Console.ReadLine();
}

В качестве домашнего задания: попробуйте поставить класс Exception перед всеми другими исключениями. Посмотрите, что в итоге получится. Если возникнут вопросы почему так произошло — задавайте в комментариях.

Блок finally — это дополнительный блок, можно написать после всех catch. Можно написать try finally и без catch. У этого блока никаких параметров нету.Блок finally гарантировано вызывается, независимо от того возникло исключение или нет.

Данный блок нужен для того, чтобы вы гарантировано могли выполнить какие-либо завершающие действия. Классика жанра: работа с файлами. Если вы открыли файл, то его нужно потом закрыть, независимо от того возникли ли какие-нибудь ошибки при работе с этим файлом (например, ошибки чтения, ошибки доступа, ошибки записи в файл). Эту операцию удобно делать в блоке finally.

Давайте опять разделим число на 0, но не будем обрабатывать ошибку. Просто добавим блок finally в конце:

static void Main(string[] args)
{
   try
   {
      Console.WriteLine(DivideNumbers(10, 0));
   }
   catch { }
   finally
   {
      Console.WriteLine("Блок finally отработал.");
   }
   Console.ReadLine();
}

Чтобы до конца загрузить вас информацией расскажу ещё про оператор throw в C#. Он обычно используется для выброса исключений. Покажу на примере, как это работает:

static void Main(string[] args)
{
   DivideNumbers(10, 0);
   Console.ReadLine();
}
static int DivideNumbers(int i1, int i2)
{
   if (i2 == 0)
   {
      throw new DivideByZeroException();
   }
   else return i1 / i2;
}

В данной статье вы узнали, про блок try catch в C# и о том, как обрабатывать исключительные ситуации.

На связи был Алексей Гулынин, оставляйте свои комментарии, увидимся в следующих статьях.


Комментарии:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *