Статьи

Какие исключения?

Этот блог некоторое время был тихим. Я сейчас пишу книгу о локализации. Я и мой партнер также приближаемся к выпуску нашего нового стартапа. Одна из его функций — автоматически перехватывать необработанные исключения и отправлять их в веб-сервис для аналитики. Но чтобы в полной мере воспользоваться этим, вам придется воспользоваться набором лучших методов обработки исключений. Поэтому я собираюсь написать ряд статей об исключениях. Серия закончится выпуском нашего сервиса.

Что такого исключительного за исключением?

Познакомьтесь с Бобом:

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

Встретиться с Сэмом

Ему не так повезло, и каждый доллар считается. Это означает, что он не просто смахивает свою кредитную карту и ожидает, что она будет работать. Обычно он заходит в банкомат, чтобы проверить баланс.

Я хочу сказать, что у них обоих был сценарий использования «Оплата товаров кредитной картой». Но так как Боб не ожидает, что карта будет пустой, он просто смахивает эту bling bling карту без предварительной проверки. Сэм, с другой стороны, ожидает, что платеж не удастся. Поэтому он удостоверится в том, что на карте достаточно денег, прежде чем ее украсть.

Теперь представьте, что кто-то взломал карту Бобса, поэтому она пуста. Он пойдет в магазин с этой широкой жирной усмешкой и будет делать покупки, как сумасшедший, как обычно. Но на этот раз его ждет сюрприз: на счету нет денег. Он понимает, что не так, но не почему. Следовательно, он ничего не может с этим поделать. Ему придется пойти домой и попытаться понять, что произошло, а также попытаться предотвратить это в будущем.

И это то, что является исключением.

Пример кода

Если у вас есть файл XML, который поставляется вместе с вашим продуктом, например, список продуктов, вы ожидаете, что он существует. Если этого не произойдет, вы ничего не можете с этим поделать. Следовательно, произошло нечто исключительное.

С другой стороны, если у вас есть файл XML, который содержит кэш самых последних использованных продуктов, вы ожидаете, что этот файл не будет существовать в первый раз. Если это не так, просто создайте его и добавьте продукт, над которым в данный момент работает пользователь.

То, что я говорю, — то, что является исключительным, зависит от варианта использования. В одном случае жизненно важно, чтобы файл существовал, в другом случае вы можете продолжить, просто создав файл.

Исключительными являются случаи, когда вы не можете спасти ситуацию, чтобы приложение продолжало работать, как ожидалось.

Если мы переведем оба случая в код, первое будет выглядеть примерно так:

public IEnumerable<Product> GetProducts()
{
    using (var stream = File.Read(Path.Combine(Environment.CurrentDirectory, "products.xml")))
	{
		var serializer = new XmlSerializer();
		return (IEnumerable<Product>)serializer.Deserialize(stream);
	}
}

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

Другой пример, с другой стороны, ожидает, что файл не существует. Поэтому мы проверяем, существует ли файл:

public IEnumerable<Product> GetCachedProducts()
{
	var fullPath = Path.Combine(Environment.CurrentDirectory, "ProductCache.xml");
	if (!File.Exists(fullPath))
		return new Product[0];
		
	using (var stream = File.Read(fullPath))
	{
		var serializer = new XmlSerializer();
		return (IEnumerable<Product>)serializer.Deserialize(stream);
	}
}

Когда ловить исключения

Так когда же вы должны ловить исключения?

Когда вы можете заставить метод возвращать то, что от него ожидается.

Давайте использовать приведенные выше примеры снова:

IEnumerable<Product> GetProducts()

Этот метод говорит, что он всегда должен возвращать список продуктов. Если вы можете сделать это, поймав исключение, сделайте это. Если вы не можете, вы не должны ловить исключения в этом.

IEnumerable<Product> GetCachedProducts()

Этот метод немного сложнее. Мы могли бы поймать  FileNotFoundException это и использовать это, чтобы обнаружить, отсутствует ли файл кэша. Это, однако, говорит о том, что мы ожидали, что файл должен существовать, но мы можем спасти ситуацию, обработав исключение. Это неправда. Мы ожидаем, что файл не существует.

Резюме

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

Если вы хотите перехватывать исключения для их регистрации, делайте это на верхнем уровне (например, WCF или ASP.NET MVC). Ваш код станет намного менее загроможденным.