什么是两者之间的区别?
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);
。 - Dirk Vollmarex.Message
,这实际上更糟糕。 Translated:
I understand, I will translate for you: @0xA3: I meant ex.Message
, which is worse. - SLaks[Serializable()]
。 - Dirk Vollmarthrow;
语句时,实际引发异常的代码行号会被替换为 throw;
所在的代码行号。您认为应该如何处理这种情况?(原问题链接:https://dev59.com/tnE95IYBdhLWcg3wCJNf) - Eric J.第一个保留原始的堆栈跟踪信息:
try { ... }
catch
{
// Do something.
throw;
}
第二种方法允许您更改异常的类型和/或消息和其他数据:try { ... } catch (Exception e)
{
throw new BarException("Something broke!");
}
还有一种方法是通过传递内部异常来处理:
try { ... }
catch (FooException e) {
throw new BarException("foo", 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字典中添加信息),同时保留原始异常中的所有信息(包括帮助链接、数据字典等)。
还有一点是我没看到其他任何人提到过的:
如果在catch {}块中什么也不做,那使用try...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;
会保留原始的堆栈跟踪,通常更加有用。唯一例外的情况是当你想要在自定义异常中包装异常时。那么你应该这样做:
catch(Exception e)
{
throw new CustomException(customMessage, e);
}
throw
语句重新抛出已捕获的异常,保留堆栈跟踪信息,而throw new Exception
会丢失某些已捕获异常的细节。
通常情况下,您可以仅使用throw
语句记录异常,而不必在此时完全处理它。
BlackWasp有一篇名为在C#中抛出异常的好文章。
throw
用于重新抛出已捕获的异常。如果您想在将异常传递到调用链之前对其进行某些操作,则可以使用此功能。
如果没有任何参数使用throw
,则保留调用堆栈以进行调试。
Throw;
: 重新抛出原始异常并保留异常类型。
Throw new exception();
: 重新抛出原始异常类型并重置异常堆栈跟踪。
Throw ex;
: 重置异常堆栈跟踪并重置异常类型。
最重要的区别是第二个表达式会擦除异常的类型。而异常类型在捕获异常时起着至关重要的作用:
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块捕获。