Большую часть времени вы можете не заботиться о типе получаемого вами исключения, поэтому перехват универсального Exception
вполне подходит, однако существуют определенные ситуации, в которых вы на самом деле хотите перехватить соответствующее исключение (а не только универсальное *)1002 *).
Один конкретный пример - если у вас есть поток, и вы хотите прервать его из блокирующего вызова, в этом случае вы должны различать InterruptException
и Exception
.
Рассмотрим этот пример: у вас есть поток, который запускает Read
каждую минуту в течение 5 минут (это не очень реалистичный пример, но он должен дать вам представление о том, почему вы хотите обрабатывать различные исключения).Вы должны остановить поток через 5 минут, потому что ваше приложение будет закрыто, и вы не хотите ждать еще минуту, пока не будет прочитан флаг running
... в конце концов, вы не хотите, чтобы ваш пользовательждать целую минуту, чтобы просто закрыть приложение.Чтобы сразу же остановить поток, вы устанавливаете флаг в false и вызываете Interrupt
в своей теме.В этом случае вам специально нужно перехватить исключение ThreadInterrupted
, поскольку оно говорит вам, что вы должны выйти из цикла.Если вы поймали другое исключение, значит, вам не удалось выполнить задачу, но вы не хотите отказываться от задания все вместе, и вы хотели бы попытаться прочитать снова в следующую минуту.Это показывает, как ваши требования определяют тип исключений, которые вы должны обработать.Вот пример в коде:
bool running = true;
Thread t = new Thread(()=>
{
while(running)
{
try
{
// Block for 1 minute
Thread.Sleep(60*1000);
// Perform one read per minute
personsReader.Read(filename, persons);
}
catch (KeyNotFoundException e)
{
// Perform a specific exception handling when the key is not found
// but do not exit the thread since this is not a fatal exception
MessageBox.Show(e.Message);
}
catch(InterruptException)
{
// Eat the interrupt exception and exit the thread, because the user
// has signalled that the thread should be interrupted.
return;
}
catch(Exception e)
{
// Perform a genetic exception handling when another exception occurs
// but do not exit the thread since this is not a fatal error.
MessageBox.Show("A generic message exception: " + e.Message);
}
}
});
t.IsBackground = true;
t.Start();
// Let the thread run for 5 minutes
Thread.Sleep(60*5000);
running = false;
// Interrupt the thread
t.Interrupt();
// Wait for the thread to exit
t.Join();
Теперь перейдем к другой проблеме, за исключением того, что она не отображается: обратите внимание, что вы получаете доступ к person[e.Name.ToString()] = e.Value
, который требует поиска ключа, и если ключ не находится вкарта, то вы можете получить KeyNotFoundException
.Это будет общее исключение, которое вы перехватываете, и ваше пользовательское исключение никогда не будет выдано, потому что person[e.Name.ToString()]
может выдать даже прежде, чем вы доберетесь до своего кода.
foreach (XElement e in person)
person[e.Name.ToString()] = e.Value; // <-- May be throwing the KeyNotFoundException
if (person["Name"] == null || person["Job"] == null || person["HairColor"] == null)
throw new KeyNotFoundException("Person element not found.");
Кроме того, вы не хотите бросать KeyNotFoundException
, когда вы действительно нашли ключ, но не нашли соответствующего значения: если person["Name"] == null
оценивается как true, тогдаКлюч «Имя» фактически был найден в словаре person
, поэтому бросание KeyNotFoundException
может ввести в заблуждение любого, кто поймает это исключение.В случае, если ваше значение равно null
, тогда, вероятно, не рекомендуется создавать исключение в любом случае ... это действительно не исключительный случай.Вы можете вернуть флаг, указывающий, что ключ не был найден:
public bool PerformRead(/*... parameters ...*/)
{
foreach (XElement e in person)
{
// Avoid getting the KeyNotFoundException
if(!person.ContainsKey(e.Name.ToString()))
{
person.Add(e.Name.ToString(), "some default value");
}
person[e.Name.ToString()] = e.Value;
}
if (person["Name"] == null || person["Job"] == null || person["HairColor"] == null)
{
return false;
}
else
{
return true;
}
}