gpt4 book ai didi

c# - C# 中的(普通)throw 语句会导致异常吗?

转载 作者:可可西里 更新时间:2023-11-01 03:06:53 25 4
gpt4 key购买 nike

问题:C# 中的普通 throw 语句本身是否会导致新的异常?


请注意,我问这个问题是出于好奇,而不是因为我有任何实际或现实世界的情况会很重要。另请注意,我的直觉和经验告诉我答案是“否”,但我希望以某种方式验证该答案(请参阅到目前为止我尝试过的资源)。

下面是一些示例代码来说明我的问题:

try
{
int x = 0, y = 1 / x;
}
catch (Exception outerException)
{

try
{
throw;
}
catch (Exception innerException)
{
// Q: Does this Assert ever fail??
System.Diagnostics.Debug.Assert(outerException.Equals(innerException));
}
}

我想知道是否有任何方法可以改变环境,使 Assert 失败,而不触及内部 try/catch block 。

我已经尝试或正在尝试回答这个问题:

  • 阅读 throw (C# Reference) MSDN 上的页面 - 没有明确的答案;
  • 已检查 C# Language Specification 的第 5.3.3.11 部分- 这可能是寻找此类信息的错误位置;
  • 掩盖了我可以尝试在 throw 语句上触发的异常。想到了 OutOfMemoryException,但在 throw 时很难触发。
  • 打开 ILDASM 检查生成的代码。我可以看到 throw 转换为 rethrow 指令,但我不知道去哪里进一步检查该语句是否可以或不能抛出异常。

这是 ILDASM 为内部 try 位显示的内容:

.try
{
IL_000d: nop
IL_000e: rethrow
} // end .try

因此,总结一下:throw 语句(用于重新抛出异常)是否会导致自身异常?

最佳答案

老实说,理论上断言可以“失败”(实际上我不这么认为)。

如何?

注意:以下只是我根据我之前对 SSCLI 所做的一些研究得出的“意见”。

  1. InvalidProgramException可能发生。诚然,这是极不可能的,但理论上是可能的(例如,某些内部 CLR 错误可能导致可抛出对象变得不可用!!!!)。
  2. 如果 CLR 没有找到足够的内存来处理“重新抛出”操作,它将抛出 OutOfMemoryException(如果 CLR 的内部重新抛出逻辑不处理“预分配”异常,则需要分配一些内存,例如OutOfMemoryException)。
  3. 如果 CLR 在其他主机下运行(例如 SQL 服务器甚至您自己的主机)并且主机决定终止异常重新抛出线程(基于某些内部逻辑)ThreadAbortException(称为粗鲁线程中止在这种情况下)将被提高。不过,我不确定在这种情况下 Assert 是否会执行。
  4. 自定义主机可能已将升级策略应用于 CLR (ICLRPolicyManager::SetActionOnFailure)。在这种情况下,如果您正在处理 OutOfMemoryException,升级策略可能会导致 ThreadAbortException 发生(再次粗鲁的线程中止。不确定如果策略指示正常的线程中止会发生什么)。
  5. 虽然@Alois Kraus 澄清了“正常”线程中止异常是不可能的,但根据 SSCLI 研究,我仍然怀疑(正常)ThreadAbortException 是否会发生。

编辑:

正如我之前所说,断言在理论上可能会失败,但实际上这种情况极不可能发生。因此,很难为此开发 POC。为了提供更多“证据”,以下是用于处理 rethow IL 指令的 SSCLI 代码片段,这些片段验证了我的上述观点。

警告:商业 CLR 可能与 SSCLI 有很大不同。

  1. 无效程序异常:

    if (throwable != NULL)
    {
    ...
    }
    else
    {
    // This can only be the result of bad IL (or some internal EE failure).
    RealCOMPlusThrow(kInvalidProgramException, (UINT)IDS_EE_RETHROW_NOT_ALLOWED);
    }
  2. 粗鲁的线程中止:

    if (pThread->IsRudeAbortInitiated())
    {
    // Nobody should be able to swallow rude thread abort.
    throwable = CLRException::GetPreallocatedRudeThreadAbortException();
    }

    这意味着如果启动了“粗鲁的线程中止”,任何异常都会更改为粗鲁的线程中止异常。

  3. 现在最有趣的是 OutOfMemoryException。由于重新抛出 IL 指令本质上是重新抛出相同的异常对象(即 object.ReferenceEquals 返回 true),因此似乎不可能在重新抛出时发生 OutOfMemoryException。但是,以下 SSCLI 代码表明这是可能的:

     // Always save the current object in the handle so on rethrow we can reuse it. This is important as it
    // contains stack trace info.
    //
    // Note: we use SafeSetLastThrownObject, which will try to set the throwable and if there are any problems,
    // it will set the throwable to something appropiate (like OOM exception) and return the new
    // exception. Thus, the user's exception object can be replaced here.

    throwable = pThread->SafeSetLastThrownObject(throwable);

    SafeSetLastThrownObject 调用 SetLastThrownObject,如果失败则引发 OutOfMemoryException。这是 SetLastThrownObject 的片段(添加了我的评论)

    ...
    if (m_LastThrownObjectHandle != NULL)
    {
    // We'll somtimes use a handle for a preallocated exception object. We should never, ever destroy one of
    // these handles... they'll be destroyed when the Runtime shuts down.
    if (!CLRException::IsPreallocatedExceptionHandle(m_LastThrownObjectHandle))
    {
    //Destroys the GC handle only but not the throwable object itself
    DestroyHandle(m_LastThrownObjectHandle);
    }
    }
    ...

    //This step can fail if there is no space left for a new handle
    m_LastThrownObjectHandle = GetDomain()->CreateHandle(throwable);

    以上代码片段显示可抛出对象的 GC 句柄被销毁(即释放 GC 表中的一个槽),然后创建一个新句柄。由于一个槽刚刚被释放,新句柄的创建永远不会失败,直到在一个非常罕见的情况下,新线程在正确的时间被调度并消耗所有可用的 GC 句柄。

除此之外,所有异常(包括重新抛出)都通过 RaiseException 引发赢API。捕获此异常以准备相应托管异常的代码本身可以引发 OutOfMemoryException

关于c# - C# 中的(普通)throw 语句会导致异常吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11195925/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com