Страницы

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

воскресенье, 2 февраля 2020 г.

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

#c_sharp #net #async_await


Вызываю метод, который при искл.чение перебрасывает его наверх, чтобы обработать
во внешнем коде 

    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("Ошибка при обработке данных внешнего сервиса\n" + ex.Message);
}


Но исключение не обрабатываеися и выскакивает в отладчике. В чем может быть причина?
    


Ответы

Ответ 1



Во время выполнения под отладчиком выпадают все исключения, отмеченные в списке 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("Ошибка при обработке данных внешнего сервиса\n" + ex.Message); }

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

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