Вызываю метод, который при искл.чение перебрасывает его наверх, чтобы обработать во внешнем коде
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);
}
Комментариев нет:
Отправить комментарий