Я читал, что когда в блоке catch я могу перебросить текущее исключение, используя "throw"; или "выбросить";
"Чтобы сохранить исходную информацию трассировки стека за исключением, используйте инструкцию throw без указания исключения".
Я получаю три разных стека. Я ожидал, что первый и второй трассы будут одинаковыми. Что я делаю не так? (или понимаете неправильно?)
System.Exception: test at ConsoleApplication1.Program.Main(String [] args) в c:\Program.cs: строка 13 System.Exception: test at ConsoleApplication1.Program.Main(String [] args) в c:\Program. cs: строка 16 System.Exception: test at ConsoleApplication1.Program.Main(String [] args) в c:\Program.cs: строка 20
Ответ 3
Ну, я немного выкопал эту проблему, и вот мой очень личный вывод:
Никогда не используйте "бросок"; но всегда реконструируйте новое исключение с указанной причиной.
Вот мои рассуждения:
Я был под влиянием моего предыдущего опыта работы с Java и ожидал, что С# throw будет очень похож на Java. Ну, я немного порылся в этом вопросе, и вот мои наблюдения:
static void Main(string[] args){
try {
try {
throw new Exception("test"); // 13
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw ex;// 17
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
}
Урожайность:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17
Интуитивно, Java-программист ожидал, что оба исключения будут одинаковыми:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
Но документация на С# ясно говорит, что это то, что следует ожидать:
"Если исключение повторно выбрано, указав исключение в выражении throw, трассировка стека будет перезапущена в текущем методе и список вызовов методов между исходным методом, который забросил исключение, и текущий метод потерян. исходную информацию трассировки стека с исключением, используйте инструкцию throw без указания исключения ".
Теперь, если я немного изменил тест (заменив throw ex; by throw, в строке 17).
try {
try {
throw new Exception("test"); // 13
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw;// 17
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
Урожайность:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17
Очевидно, это не то, что я ожидал (поскольку это был исходный вопрос). Я потерял исходную точку бросания во второй трассе стека. Саймон Уайтхед связал объяснение, которое является тем броском; сохраняет трассировку стека только в том случае, если исключение не произошло в текущем методе. Таким образом, "throw" без параметра в том же методе довольно бесполезен, поскольку, как правило, это не поможет вам найти причину исключения.
Выполняя то, что сделает любой Java-программист, я заменил оператор в строке 17 на:
throw new Exception("rethrow", ex);// 17
Урожайность:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: rethrow ---> System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
--- End of inner exception stack trace ---
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17
что является гораздо лучшим результатом.
Затем я начал тестировать вызовы методов.
private static void throwIt() {
throw new Exception("Test"); // 10
}
private static void rethrow(){
try{
throwIt(); // 15
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw; // 18
}
}
static void Main(string[] args){
try{
rethrow(); // 24
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
}
Опять же, трассировки стека не были тем, что ожидал я (программист Java). В Java оба стека были бы одинаковыми, а три метода были бы такими же:
java.lang.Exception: Test
at com.example.Test.throwIt(Test.java:10)
at com.example.Test.rethrow(Test.java:15)
at com.example.Test.main(Test.java:24)
Первая трассировка стека имеет только два метода.
System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow() in Program.cs:line 15
Это похоже на то, что трассировка стека была заполнена как часть процесса разворачивания стека. Если бы я должен был регистрировать трассировку стека, чтобы исследовать исключение в этой точке, Id, вероятно, не будет иметь важной информации.
Вторая трассировка стека - это три метода, но строка 18 (throw;) появляется в нем.
System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow() in Program.cs:line 18
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 24
Это наблюдение аналогично предыдущему: трассировка стека не сохраняется для области текущего метода, и снова я теряю вызываемый метод, в котором происходит исключение. Например, если rethow было написано как:
private static void rethrow(){
try{
if (test)
throwIt(); // 15
else
throwIt(); // 17
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw; // 20
}
}
Урожайность
System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow() in Program.cs:line 20
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26
Какой вызов throwIt() выбрал Exception? Stack говорит строку 20, так ли это строка 15 или 17?
Решение такое же, как и предыдущее: оберните причину в новом исключении, которое дает:
System.Exception: rethrow ---> System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 17
--- End of inner exception stack trace ---
at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 20
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26
Мой простой вывод ко всему этому - никогда не использовать "бросок"; но всегда восстанавливать новое исключение с указанной причиной.