- Отличать ошибку компиляции от исключения во время выполнения
- Перехватывать исключения с помощью
try, нескольких блоковcatchиfinally - Выбрасывать исключение через
throwи создавать собственный класс исключения - Выбирать для ожидаемых ошибок такие способы, как
TryParse
Компилятор находит многие ошибки ещё до запуска программы, но не все. Пользователь вводит «abc» вместо возраста, файл удалён, пропал интернет, кто-то делит на ноль — такие проблемы проявляются только во время работы программы. В C# в таких случаях возникает исключение (exception). Если его не перехватить, программа остановится с сообщением «Unhandled exception»; если перехватить — можно понятно объяснить проблему пользователю и продолжить работу.
try и catch
Код, который может завершиться ошибкой, помещают в блок try. Как только возникает исключение, оставшиеся строки try пропускаются и управление переходит в подходящий блок catch. Каждый catch ловит определённый тип исключения, поэтому на разные ошибки можно реагировать по-разному.
string[] inputs = ["42", "abc", "99999999999"];
foreach (string input in inputs)
{
try
{
int number = int.Parse(input);
Console.WriteLine($"OK: {number}");
}
catch (FormatException)
{
Console.WriteLine($"'{input}' is not a number");
}
catch (OverflowException)
{
Console.WriteLine($"'{input}' is too big for int");
}
}OK: 42 'abc' is not a number '99999999999' is too big for int
Исключение — тоже объект: в catch (IndexOutOfRangeException ex) переменная ex содержит его данные, например ex.Message — текст пояснения. Все исключения наследуются от класса Exception, поэтому catch (Exception ex) ловит любую ошибку — пиши его всегда последним. Блок finally выполняется всегда, была ошибка или нет: в нём удобно закрыть файл или освободить ресурс.
int[] scores = [90, 75, 88];
try
{
Console.WriteLine(scores[5]);
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
Console.WriteLine("This line always runs.");
}
try
{
int zero = 0;
Console.WriteLine(10 / zero);
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}Error: Index was outside the bounds of the array. This line always runs. DivideByZeroException: Attempted to divide by zero.
| Исключение | Когда возникает |
|---|---|
FormatException | int.Parse("abc") — текст не является числом |
DivideByZeroException | целое число делится на ноль |
IndexOutOfRangeException | индекс, которого нет в массиве |
KeyNotFoundException | ключ, которого нет в словаре |
NullReferenceException | обращение к члену переменной, равной null |
FileNotFoundException | открываемого файла не существует |
throw: генерация исключения
Если метод не может правильно выполнить свою работу, он должен выбросить исключение через throw, а не тихо вернуть неверный результат. Готовых типов много: ArgumentException — неверный аргумент, InvalidOperationException — операция, недопустимая в текущем состоянии объекта. При необходимости можно создать собственный класс исключения, наследуемый от Exception, — настоящее применение наследования из прошлого урока.
var wallet = new Wallet(100);
try
{
wallet.Pay(30);
wallet.Pay(250);
wallet.Pay(10);
}
catch (NotEnoughMoneyException ex)
{
Console.WriteLine($"Payment failed: {ex.Message}");
}
Console.WriteLine($"Left: {wallet.Balance} AZN");
class NotEnoughMoneyException(string message) : Exception(message);
class Wallet(decimal balance)
{
public decimal Balance { get; private set; } = balance;
public void Pay(decimal amount)
{
if (amount > Balance)
throw new NotEnoughMoneyException($"need {amount} AZN, have {Balance} AZN");
Balance -= amount;
Console.WriteLine($"Paid {amount} AZN");
}
}Paid 30 AZN Payment failed: need 250 AZN, have 70 AZN Left: 70 AZN
wallet.Pay(10) так и не выполнился: после исключения остаток блока try пропускается.Когда не стоит использовать исключения
Исключения — для непредвиденных ситуаций. А то, что пользователь введёт что-то неправильно, вполне ожидаемо, поэтому для проверки текста int.TryParse лучше, чем try/catch: код короче, работает быстрее и ясно показывает намерение. То же правило действует и для словарей: TryGetValue проверяет отсутствие ключа без всякой ошибки.
string[] answers = ["15", "fifteen", "-3"];
foreach (string answer in answers)
{
if (!int.TryParse(answer, out int age))
Console.WriteLine($"'{answer}': please type digits");
else if (age is < 0 or > 120)
Console.WriteLine($"'{answer}': age must be 0-120");
else
Console.WriteLine($"Age accepted: {age}");
}Age accepted: 15 'fifteen': please type digits '-3': age must be 0-120
Главное
- Исключение — ошибка во время работы программы; если его не перехватить, программа остановится.
- Когда в блоке
tryвозникает исключение, оставшиеся строки пропускаются и срабатывает подходящийcatch. - Частные блоки
catchпишут первыми,catch (Exception)— последним;finallyвыполняется всегда. throw new ...Exception("...")сообщает о недопустимой ситуации; собственные исключения наследуй отException.- Для ожидаемых ошибок
TryParseиTryGetValueлучше исключений.
Проверь себя
Вопросов: 10. Каждый правильный ответ приносит XP.
int.Parse("abc")?