Страницы

Поиск по вопросам

пятница, 5 апреля 2019 г.

Не перехватывается выброшенное исключение

Вызываю метод, который при искл.чение перебрасывает его наверх, чтобы обработать во внешнем коде
private async void FillCurrentWeather(string place) { XmlSerializer serializer = new XmlSerializer(typeof(Current)); string xml = await GetWeather(CurrentWeatherUrl,place,7); if (xml != null) { try { TextReader reader = new StringReader(xml); Current forecast = (Current)serializer.Deserialize(reader); PlaceName.Content = forecast.City.Name + "," + forecast.City.Country; CurrentWeather.Update(forecast); reader.Dispose(); } catch (InvalidOperationException ex) { throw new InvalidOperationException(ex.Message, ex.InnerException); } } }
Внешний код
try { FillCurrentWeather(place); FillDayWeather(place); FillWeekWeather(place); } catch (InvalidOperationException ex) { MessageBox.Show("Ошибка при обработке данных внешнего сервиса
" + ex.Message); }
Но исключение не обрабатываеися и выскакивает в отладчике. В чем может быть причина?


Ответ

Во время выполнения под отладчиком выпадают все исключения, отмеченные в списке Debug -> Windows -> Exception Settings (Ctrl+Alt+E). Это нормально. Если вы продолжите выполнение программы (F5), то дальше отработают ваши catch блоки.
UPD
С добавление полного кода стала понятна настоящая причина, к которой предыдущий абзац не имеет никакого отношения. Проблема в этом:
async void FillCurrentWeather
Для полного понимания того, как работает async/await, отсылаю к видео. Здесь же вкратце опишу, что происходит при выполнении программы. Итак:
В блоке try внешнего кода вызывается метод FillCurrentWeather(place); Выполнение метода FillCurrentWeather доходит до этой строки и покидает его*:
string xml = await GetWeather(CurrentWeatherUrl,place,7); В блоке try внешнего кода отрабатывают все остальные методы. Метод GetWeather завершает выполнение, продолжается выполнение метода FillCurrentWeather, вываливается исключение. НО! Блок try/catch во внешнем коде уже завершился, он не может поймать это исключение! Поэтому исключение вываливается на самом верхнем уровне как необработанное (UnhandledTaskException).
async void опасны именно по этим причинам:
мы не знаем, когда они на самом деле завершаются соответственно мы не может адекватно реагировать на исключения в таких методах
async void сигнатуру можно использовать только для событий и методов, имеющих событийную семантику, плюс не забывать целиком оборачивать все тело таких методов в try/catch, поскольку если возникнет исключение, вы либо получите необработанное исключение (если возникло в UI потоке), либо, в случае если оно возникло в потоке пула, вы об этом или не узнаете, или это исключение убьет процесс (в случае с .NET 4.0 будет именно так).
Какие у вас варианты решения? В данном случае -- только один: изменить сигнатуру метода на async Task и ожидать завершения этого метода во внешнем коде.
И еще маленький совет: в вашем случае блок try/catch в методе FillCurrentWeather вообще не нужен, поэтому что он по сути ничего не делает. Просто уберите его. (А впредь, если хотите пробрасывать исключения, оборачивая их в другие, всегда включайте исключение целиком, а не его InnerException, которого может и не быть: throw new Exception("This is new exception", e)).

*на самом деле это упрощение, и выполнение продолжится вглубь до того момента, когда вызов уйдет к IO-устройству. Важно лишь, что программа не дожидается завершения этого метода.
UPD2
Чтобы не терять параллелизацию получения данных о погоде, которая невольно достигалась при использовании async void методов, нужно использовать Task.WhenAll
try { var currentTask = FillCurrentWeather(place); var dayTask = FillDayWeather(place); var weekTask = FillWeekWeather(place); await Task.WhenAll(currentTask, dayTask, weekTask); } catch (InvalidOperationException ex) { MessageBox.Show("Ошибка при обработке данных внешнего сервиса
" + ex.Message); }

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

Отправить комментарий