.NET/C# 異常處理:寫一個空的 try 塊代碼,而把重要代碼寫到 finally 中


不知你是否見過 try { } finally { } 代碼中,try 塊留空,而只往 finally 中寫代碼的情況呢?這種寫法有其特殊的目的。

本文就來說說這種不一樣的寫法。


你可以點開這個鏈接查看 Exception 類,在里面你可以看到一段異常處理的代碼非常奇怪:

// 代碼已經過簡化。 internal void RestoreExceptionDispatchInfo(ExceptionDispatchInfo exceptionDispatchInfo) { // 省略代碼。 try{} finally { // 省略代碼。 } // 省略代碼。 } 

神奇之處就在於,其 try 塊是空的,重要代碼都放在 finally 中。那為什么會這么寫呢?

在代碼注釋中的解釋為:

We do this inside a finally clause to ensure ThreadAbort cannot be injected while we have taken the lock. This is to prevent unrelated exception restorations from getting blocked due to TAE.

翻譯過來是:

在 finally 子句中執行此操作以確保在獲取鎖時無法注入 ThreadAbort。這是為了防止不相關的異常恢復因 TAE 而被阻止。

也就是說,此方法是為了與 Thread.Abort 對抗,防止 Thread.Abort 中斷此處代碼的執行。Thread.Abort 的執行交給 CLR 管理,finally 的執行也是交給 CLR 管理。CLR 確保 finally塊執行的時候不會被 Thread.Abort 阻止。

代碼在 .NET Core 和 .NET Framework 中的實現完全一樣:

// This is invoked by ExceptionDispatchInfo.Throw to restore the exception stack trace, corresponding to the original throw of the // exception, just before the exception is "rethrown". [SecuritySafeCritical] internal void RestoreExceptionDispatchInfo(System.Runtime.ExceptionServices.ExceptionDispatchInfo exceptionDispatchInfo) { bool fCanProcessException = !(IsImmutableAgileException(this)); // Restore only for non-preallocated exceptions if (fCanProcessException) { // Take a lock to ensure only one thread can restore the details // at a time against this exception object that could have // multiple ExceptionDispatchInfo instances associated with it. // // We do this inside a finally clause to ensure ThreadAbort cannot // be injected while we have taken the lock. This is to prevent // unrelated exception restorations from getting blocked due to TAE. try{} finally { // When restoring back the fields, we again create a copy and set reference to them // in the exception object. This will ensure that when this exception is thrown and these // fields are modified, then EDI's references remain intact. // // Since deep copying can throw on OOM, try to get the copies // outside the lock. object _stackTraceCopy = (exceptionDispatchInfo.BinaryStackTraceArray == null)?null:DeepCopyStackTrace(exceptionDispatchInfo.BinaryStackTraceArray); object _dynamicMethodsCopy = (exceptionDispatchInfo.DynamicMethodArray == null)?null:DeepCopyDynamicMethods(exceptionDispatchInfo.DynamicMethodArray); // Finally, restore the information. // // Since EDI can be created at various points during exception dispatch (e.g. at various frames on the stack) for the same exception instance, // they can have different data to be restored. Thus, to ensure atomicity of restoration from each EDI, perform the restore under a lock. lock(Exception.s_EDILock) { _watsonBuckets = exceptionDispatchInfo.WatsonBuckets; _ipForWatsonBuckets = exceptionDispatchInfo.IPForWatsonBuckets; _remoteStackTraceString = exceptionDispatchInfo.RemoteStackTrace; SaveStackTracesFromDeepCopy(this, _stackTraceCopy, _dynamicMethodsCopy); } _stackTraceString = null; // Marks the TES state to indicate we have restored foreign exception // dispatch information. Exception.PrepareForForeignExceptionRaise(); } } } 

你可以在 這里 查看 .NET Framework 版本,在這里 查看 .NET Core 的版本。


參考資料

原文地址: https://walterlv.com/post/empty-try-block.html

作者:呂毅

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com