# .NET/C# 异常处理：写一个空的 try 块代码，而把重要代码写到 finally 中（Constrained Execution Regions）

### 空的 try 块

// 代码已经过简化。
internal void RestoreExceptionDispatchInfo(ExceptionDispatchInfo exceptionDispatchInfo)
{
// 省略代码。
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 而被阻止。

// 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();
}
}
}


### 受约束的执行区域（Constrained Execution Regions）

Doing so instructs the just-in-time compiler to prepare all the code in the finally block before running the try block. This guarantees that the code in the finally block is built and will run in all cases. It is not uncommon in a CER to have an empty try block. Using a CER protects against asynchronous thread aborts and out-of-memory exceptions. See ExecuteCodeWithGuaranteedCleanup for a form of a CER that additionally handles stack overflows for exceedingly deep code.