Рассмотрим следующую программу. Как поведение, которое он отображает (а именно, что исключения будут распространяться из обработчика событий), "хорошо"? Насколько я могу судить, это может быть только плохим; неожиданные исключения появляются из функций, которых они не должны. В моем конкретном случае он убивал нитки. Таким образом, это поведение действительно хорошо в некоторых случаях? Является ли это причиной того, что исключение исключений из обработчиков событий - плохой дизайн?
static class Program {
static void Main()
{
Foo foo = new Foo();
foo.SomeEvent += ThrowException;
try
{
foo.OnSomeEvent();
}
catch (Exception)
{
// This is printed out
Console.WriteLine("Exception caught!");
}
}
static void ThrowException(object sender, EventArgs e)
{
throw new Exception();
}
}
// Define other methods and classes here
class Foo
{
public event EventHandler SomeEvent;
public void OnSomeEvent()
{
SomeEvent(this, EventArgs.Empty);
}
}
Какая ваша предпочтительная альтернатива - молча усвоить исключение? Мне это совсем не понравится.
События - это всего лишь способ реализации шаблона наблюдателя. Если слушатель выдает исключение, я бы абсолютно ожидал, что это исключение будет возвращено вызывающему. Любое другое поведение, о котором я могу думать, будет эффективно рассматривать исключение как несущественное. Весь смысл исключений заключается в том, что когда что-то пойдет не так, вы узнаете об этом быстро и неявно. Вы должны явно обрабатывать исключения, чтобы вы не походили на ваш весёлый путь в коррумпированном состоянии, не осознавая этого.
Вы делаете верную точку, в чью ответственность за обработку исключений. Вообще говоря, я считаю, что лучше всего предположить, что практически все может выбросить исключение в любое время. Помимо особых случаев, когда я знаю, что может возникнуть конкретное исключение, и я могу справиться с этим, я обычно не ломаю исключения, кроме верхнего уровня, за исключением, возможно, для переноса и реверберации, или регистрации и ретронирования.
Теперь возможно, что один из ваших обработчиков событий не должен бросать исключение - что они на самом деле не запущены в условие ошибки, но что должно произойти, если это совершенно разумное исключение, которое указывает на серьезную проблему? В то время как сбой программы является уродливым, он часто лучше, чем продолжение с некоторыми из его сбоев, возможно, развращение сохраняющегося состояния и т.д.
В принципе, я не думаю, что в поле CS/SE все-таки "правильная" обработка ошибок. Я даже не уверен, что есть элегантный способ делать правильные вещи, которые легко выразить во всех ситуациях... но я надеюсь, что нынешняя ситуация не так хороша, как только она становится.
Основной аспект обработки исключений, обсуждаемый здесь, заключается в следующем: не поймайте исключение, если вы не знаете, как его обрабатывать. Но позвольте говорить о шаблоне наблюдателя, где уведомитель испускает событие (или сигнал) об этом изменении состояния, и слушатели обрабатывают его. Хорошим примером уведомителя является нажатие кнопки "нажатие". Находит ли кнопка заботу о том, кто является слушателем и что они делают? На самом деле, нет. Если вы слушатель, и вы получили это событие, то у вас есть работа. И если вы не можете этого сделать, вам придется обработать эту ошибку или сообщить пользователю, потому что передача исключений на кнопку не имеет смысла - кнопка определенно не знает, как обрабатывать ошибки работы слушателя. И переключатель состояния смены состояний (возможно, какой-то цикл сообщений) тоже не работает - ваше приложение закончит с Main() сбой.
Как работает шаблон наблюдателя - события-эмитенты не знают, что делают их слушатели, и очень мало шансов, что они справятся с этим исключением должным образом.
Также имейте в виду, что если ваш обработчик исключений вызывает исключение, могут быть другие слушатели, которые не получат уведомления и могут привести к состоянию приложения undefined.
Итак, мой совет - поймать все исключения в обработчике событий, но выяснить, как их обрабатывать. Или иначе никто не будет.
Мои личные предубеждения заключаются в том, что не исключение исключений, как правило, плохое. Единственное "исключение" для этого правила для меня - это простые приложения, в которых непокрытое исключение обозначает процесс, вы видите ошибку и исправляете ее.
Для многопоточных приложений, если поведение по умолчанию неперехваченных исключений заключается в zap-потоках, тогда мне кажется абсурдным не ловить исключения. Следовательно, обработчики событий не должны просто вытаскивать исключение из стека и надеяться на лучшее.
Беззаботное глотание исключений, как правило, плохо также, что-то плохое, что он нуждается в исправлении. Итак, возможно, зарегистрировать сообщение, а затем вернуться?
Exception
? то есть. на верхнем уровне потока обработки, чтобы поддерживать его после проверки работоспособности.
Было бы лучше подумать об исключении как часть вашего контракта с прослушивателями событий.
Это означает, что если слушатель хорош и ловит его Исключения (он может сделать это для известных), вы в порядке.
Для остальных, неизвестных исключений или в речи Java "Исключения Runtime", вы должны быть готовы к ним так же, как если бы они произошли в вашем коде.
То, что я пытаюсь сказать, заключается в том, что если вы строите контракт с прослушивателями событий, вы не можете заставить их бросать только один тип исключения (чтобы вы могли их уничтожить), и вам нужно серьезно относиться ко всем Исключениям. В конце концов, это признаки "неправильных состояний", которые вы не хотите скрывать для потребителей.
Событие - это не что иное, как синтаксический сахар вокруг вызова функции, поэтому имеет смысл, что он будет распространяться до функции, которая вызвала событие. Что, в противном случае, должно быть поведение? Исключение должно идти куда-то.
Вам нужен контракт с источником события о том, может ли обработчик событий генерировать исключения. Например, если COM-объект является источником события, это строго запрещено - исключения никогда не должны пересекать границу COM.
Exception
? Один конкретный случай, который приходит мне на ум в моем текущем проекте, - это обработка пользовательских сценариев. Предполагая, что они компилируются вообще, когда я пытаюсь их запустить, у меня есть блок try / catch (Exception) вокруг вызова метода, который они только что создали. Конечно, если возникает ошибка, она сообщается пользователю, чтобы он мог исправить свой сценарий, но действительно ли это дает сбой?