в чем разница между
try { ... }
catch{ throw }
и
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
Независимо от того, что второй показывает сообщение?
в чем разница между
try { ... }
catch{ throw }
и
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
Независимо от того, что второй показывает сообщение?
Ответы:
throw; сбрасывает исходное исключение и сохраняет исходный след стека.
throw ex;генерирует исходное исключение, но сбрасывает трассировку стека, уничтожая всю информацию трассировки стека до вашего catchблока.
throw ex;throw new Exception(ex.Message);еще хуже. Он создает новый Exceptionэкземпляр, теряя исходную трассировку стека исключения, а также его тип. (например, IOException).
Кроме того, некоторые исключения содержат дополнительную информацию (например, ArgumentException.ParamName).
throw new Exception(ex.Message); уничтожит эту информацию тоже.
В некоторых случаях вы можете захотеть обернуть все исключения в объект пользовательского исключения, чтобы вы могли предоставить дополнительную информацию о том, что делал код, когда создавалось исключение.
Для этого определите новый класс, который наследует Exception, добавьте все четыре конструктора исключений и, необязательно, дополнительный конструктор, который принимает InnerExceptionкак дополнительную информацию, так и добавьте новый класс исключений, передав exв качестве InnerExceptionпараметра . Передавая оригинал InnerException, вы сохраняете все свойства исходного исключения, включая трассировку стека.
throw new MyCustomException(myMessage, ex);конечно.
ex.Message, что это хуже.
[Serializable()].
throw;фактический номер строки, где произошло исключение, номером строки throw;. Как вы предлагаете справиться с этим? stackoverflow.com/questions/2493779/…
Первый сохраняет оригинальную трассировку стека:
try { ... }
catch
{
// Do something.
throw;
}
Второй позволяет изменить тип исключения и / или сообщения и другие данные:
try { ... } catch (Exception e)
{
throw new BarException("Something broke!");
}
Есть также третий способ, где вы передаете внутреннее исключение:
try { ... }
catch (FooException e) {
throw new BarException("foo", e);
}
Я бы порекомендовал использовать:
Еще один момент, который я не видел, чтобы кто-то высказывал:
Если вы ничего не делаете в своем блоке catch {}, делать попытку ... catch бессмысленно. Я вижу это все время:
try
{
//Code here
}
catch
{
throw;
}
Или хуже:
try
{
//Code here
}
catch(Exception ex)
{
throw ex;
}
Хуже всего:
try
{
//Code here
}
catch(Exception ex)
{
throw new System.Exception(ex.Message);
}
throwповторно генерирует перехваченное исключение, сохраняя трассировку стека, при этом throw new Exceptionтеряя некоторые детали перехваченного исключения.
Обычно вы используете throwдля регистрации исключения без полной обработки в этот момент.
У BlackWasp есть хорошая статья под названием « Исключения в C #» .
Создание нового исключения сносит текущую трассировку стека.
throw;сохранит исходную трассировку стека и почти всегда будет более полезной. Исключением из этого правила является случай, когда вы хотите обернуть Исключение в свое собственное Исключение. Затем вы должны сделать:
catch(Exception e)
{
throw new CustomException(customMessage, e);
}
throwдля отбрасывания пойманного исключения. Это может быть полезно, если вы хотите что-то сделать с исключением перед передачей в цепочку вызовов.
Использование throwбез каких-либо аргументов сохраняет стек вызовов для целей отладки.
Ваш второй пример сбросит трассировку стека исключения. Первый наиболее точно сохраняет происхождение исключения. Кроме того, вы развернули исходный тип, который является ключевым в знании того, что на самом деле пошло не так ... Если для функциональности требуется второе - например, для добавления расширенной информации или повторной упаковки со специальным типом, таким как пользовательское «HandleableException», тогда просто Убедитесь, что свойство InnerException также установлено!
Самое важное отличие состоит в том, что второе выражение стирает тип исключения. И тип исключения играет жизненно важную роль в отлове исключений:
public void MyMethod ()
{
// both can throw IOException
try { foo(); } catch { throw; }
try { bar(); } catch(E) {throw new Exception(E.message); }
}
(...)
try {
MyMethod ();
} catch (IOException ex) {
Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
Console.WriteLine ("Other error"); // [2]
}
Если foo()бросает IOException, [1]блок catch поймает исключение. Но когда bar()броски IOException, он будет преобразован в простого Exceptionмуравья, который не будет пойман [1]блоком catch.
throw или throw ex, оба используются для выброса или повторного выброса исключения, когда вы просто регистрируете информацию об ошибке и не хотите отправлять какую-либо информацию вызывающей стороне, вы просто регистрируете ошибку в catch и exit. Но если вы хотите отправить значимую информацию об исключении вызывающей стороне, которую вы используете throw или throw ex. Теперь разница между throw и throw ex заключается в том, что throw сохраняет трассировку стека и другую информацию, но throw ex создает новый объект исключения, и, следовательно, исходная трассировка стека теряется. Поэтому, когда мы должны использовать throw и throw e, есть еще несколько ситуаций, в которых вы можете захотеть перебросить исключение, такое как сброс информации о стеке вызовов. Например, если метод находится в библиотеке, и вы хотите скрыть детали библиотеки из вызывающего кода, Вы не обязательно хотите, чтобы стек вызовов включал информацию о частных методах в библиотеке. В этом случае вы можете перехватывать исключения в открытых методах библиотеки, а затем перебрасывать их так, чтобы стек вызовов начинался с этих открытых методов.
Ни один из ответов здесь не показывает разницу, которая может быть полезна для людей, пытающихся понять разницу. Рассмотрим этот пример кода:
using System;
using System.Collections.Generic;
namespace ExceptionDemo
{
class Program
{
static void Main(string[] args)
{
void fail()
{
(null as string).Trim();
}
void bareThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw;
}
}
void rethrow()
{
try
{
fail();
}
catch (Exception e)
{
throw e;
}
}
void innerThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw new Exception("outer", e);
}
}
var cases = new Dictionary<string, Action>()
{
{ "Bare Throw:", bareThrow },
{ "Rethrow", rethrow },
{ "Inner Throw", innerThrow }
};
foreach (var c in cases)
{
Console.WriteLine(c.Key);
Console.WriteLine(new string('-', 40));
try
{
c.Value();
} catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
}
Который генерирует следующий вывод:
Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
--- End of inner exception stack trace ---
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Голый бросок, как указано в предыдущих ответах, ясно показывает как исходную строку кода, который не прошел (строка 12), так и две другие точки, активные в стеке вызовов, когда произошло исключение (строки 19 и 64).
Вывод случая повторного выброса показывает, почему это проблема. Когда исключение перебрасывается подобным образом, исключение не будет включать информацию об исходном стеке. Обратите внимание, что throw eвключены только (строка 35) и самая внешняя точка стека вызовов (строка 64). Было бы трудно отследить метод fail () как источник проблемы, если вы выбрасываете исключения таким образом.
Последний случай (innerThrow) является наиболее сложным и включает в себя больше информации, чем любой из вышеперечисленных. Поскольку мы создаем новое исключение, мы получаем возможность добавить контекстную информацию («внешнее» сообщение здесь, но мы также можем добавить в словарь .Data новое исключение), а также сохранить всю информацию в оригинале. исключение (включая справочные ссылки, словарь данных и т. д.).