[英]Is there any real world reason to use throw ex?
在C#中, throw ex
幾乎總是錯誤的,因為它會重置堆棧跟蹤。
我只是想知道,這有什么現實世界嗎? 我能想到的唯一原因是隱藏你的封閉庫的內部,但這是一個非常薄弱的原因。 除此之外,我從未在現實世界中遇到過。
編輯:我的意思是throw ex
,就像拋出完全相同的異常,但是使用空堆棧跟蹤,就像完全錯誤一樣。 我知道throw ex必須作為一個語言構造存在,以允許拋出一個不同的異常( throw new DifferentException("ex as innerException", ex)
),並且只是想知道是否存在一個throw ex
沒有錯誤的throw new DifferentException("ex as innerException", ex)
。
我從未見過它是故意使用的,但那當然沒有任何意義。
讓我們來看看替代方案:
catch(Exception ex)
{
// do stuff (logging)
throw; // a) continue ex
throw new SomeException("blah", ex); // b) wrap
throw new SomeException("blah"); // c) replace
throw ex; // d) reset stack-trace
}
c)和d)都在放棄堆棧跟蹤,d)唯一的“優勢”我可以看到throw ex;
保留ex
的確切類型和可能的額外屬性(SQL錯誤)。
那么有沒有一種情況你想要保留除堆棧跟蹤之外的所有異常信息? 不正常,但有些猜測:
如果你停下來思考它, throw ex
實際上經常使用 - 除了一個未命名的局部變量。 (重新)設計語言將非常困難,因此:
throw new ArgumentException();
是有效的,但是
var ex = new ArgumentException();
throw ex;
無效(然后擴展它以跟蹤更復雜的變量賦值)。 即throw ex
是throw語句的“正常”形式,但是在重新拋出已經捕獲的異常時通常是錯誤的。
沒有正當理由重新拋出異常對象,即'throw ex'。
這是一種語言\\編譯器功能,雖然在實踐中沒有增加任何價值,但卻沒有增加任何價值。 這是一種類似的情況,能夠編寫代碼來訪問空引用的成員 - 它當然可能但沒有價值,例如:
//Possible but not useful.
object ref1 = null;
ref1.ToString();
能夠重新拋出異常對象是不幸的,並且經常被新手和有經驗的編碼人員誤解,直到你在生產中得到一個未處理的異常並記錄了截斷的堆棧跟蹤。
有一種微弱的感覺,它可以用來故意隱藏堆棧跟蹤信息,但拋出一個新的異常將是正確的方法來做到這一點。
我甚至可以說能夠重新拋出異常對象(即'throw ex')是一個設計缺陷 - 這樣做太容易做錯了。 但是,我懷疑這是出於性能原因而在編譯器中進行設計權衡,因為它會產生開銷來驗證'throw ex'不是're-throw'。 我敢肯定有很多這樣的權衡。
throw ex
只是一個錯誤或由不知道或忘記throw;
人做的錯字 throw;
。
它可能僅在一種情況下,在其答案中由其他人指出:您不希望將正確的堆棧跟蹤發送給調用者的情況,但您仍希望保留先前拋出的異常類型。 在實踐中,我很難想象有人會需要它的情況,並且不會使用throw new SomeCustomException(...)
。
FxCop規則CA2200 RethrowToPreserveStackDetails
以相同的方式進行,邀請您使用throw;
正確地重新拋出異常 throw;
:
try
{
// ...
}
catch (SomeException ex)
{
Logger.AddException(ex);
throw;
}
以下是重新拋出現有異常對象的兩個實際應用程序:
通過反射調用代碼時,拋出的任何異常都將包含在TargetInvocationException中 。 但是你可能想要/需要處理反射“障礙”之外的異常。 在這種情況下,包裝將使異常處理代碼復雜化。 相反,您可以打開原始異常並重新拋出。
如果您使用的是.NET 4.5+,則可以使用ExceptionDispatchInfo來保留原始堆棧跟蹤:
MethodInfo someMethod = ...
try
{
someMethod.Invoke();
}
catch (TargetInvocationException e)
{
ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
在.NET Task API中,在執行任務期間拋出的異常包含在AggregateException中 。 當使用async / await時,框架將在封面下執行展開,以便拋出原始異常而不是包裝器AggregateException。
除了在自動解包時需要重新拋出現有異常的BCL代碼之外,有些情況下您無法使用async / await,因此需要手動解包和重新拋出。 對於這一個實際的例子,檢查出的內部使用TaskExtensions.WaitAndUnwrapException在AsyncContext斯蒂芬Clearys優秀的執行AsyncEx項目。
您可能希望記錄異常,記錄它然后將其作為'throw ex'拋出到上層。 您可能還希望在此層中進行日志記錄,但這可能與您獲得異常的層無關,或者您可能不需要異常的詳細信息,因為您已經記錄了它。
讓我們說你有:
Public int GetAllCustomers()
{
try
{
return data.GetAllCustomers()
}
catch()
{
Logger.log("Error calling GetAllCustomers");
}
}
//data
Public static int GetAllCustomers()
{
try
{
//db operations...
}
catch(Exception ex)
{
Logger.log(ex.Stacktrace);
throw ex;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.