C#中的(plain)throw语句可能会导致异常吗? [英] Can the (plain) throw statement in C# cause exceptions?

查看:122
本文介绍了C#中的(plain)throw语句可能会导致异常吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题: C#中的简单 throw 语句本身是否会引起新的异常?

Question: Can the plain throw statement in C# ever cause a new exception in itself?

请注意,我从好奇心中提出这个问题,不是因为我有任何实际或现实世界情况会很重要的。还要注意,我的直觉感受和经验告诉我,答案是否,但我正在寻找某种方式验证该答案(请参阅我进一步尝试的来源)。

Note that I ask this question out of curiosity, not because I have any practical or real-world situation where it would matter much. Also note that my gut feeling and experience tell me that the answer is "No", but I'm looking to validate that answer somehow (see further down on sources I've tried so far).

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

Here's some sample code to illustrate my question:

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 块。

I'm wondering if there's any way at all to alter the circumstances such that the Assert will fail, without touching the inner try/catch block.

我试过或试图回答这个问题:

What I've tried or was looking to try to answer this:


  • 阅读MSDN上的 throw(C#参考)页面 - 没有确定的答案;

  • 已查看的部分5.3.3.11 C#语言规范 - 这可能是查找这种信息的错误的地方;

  • 通过我可以尝试触发throw语句的异常, OutOfMemoryException被提到,但是在 throw 时,很难触发。

  • 打开ILDASM以检查生成代码。我可以看到, throw 转换为 rethrow 指令,但是我丢失了进一步查看是否语句可以或不能抛出异常。

  • Read the throw (C# Reference) page on MSDN - no definitive answer;
  • Checked part 5.3.3.11 of the C# Language Specification - which is probably the wrong place to look for this kind of info;
  • Glossed through the exceptions that I could try to trigger on the throw statement. The OutOfMemoryException comes to mind, but is kind of hard to trigger at the time of the throw.
  • Opened up ILDASM to check the generated code. I can see that throw translates to a rethrow instruction, but I'm lost where to look further to check if that statement can or cannot throw an exception.

这是ILDASM显示的内部 try 位:

This is what ILDASM shows for the inner try bit:

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

所以,总结一下:可以一个throw语句导致异常)原因一个异常本身?

So, to summarize: can a throw statement (used to rethrow an exception) ever cause an exception itself?

推荐答案

在我看来,从理论上讲, assert可以失败(实际上我不这么认为)。

In my honest opinion, theoretically the assert can 'fail' (practically I don't think so).

如何?

注意:在我之前在SSCLI上进行的一些研究的基础上,只是我的意见。

Note: Below are just my 'opinion' on the basis of some research I earlier did on SSCLI.


  1. An InvalidProgramException 。这肯定是非常不可能,但理论上可能(例如一些内部CLR错误可能导致可抛物体变得不可用!!!!)。

  2. 如果CLR没有找到足够的内存来处理重新抛出操作,它将抛出一个OutOfMemoryException(CLR的内部重新抛出逻辑需要分配一些内存,如果它不是交易使用预分配异常,如OutOfMemoryException)。

  3. 如果CLR在某个其他主机(例如SQL服务器,甚至是您自己的)下运行,并且主机决定终止异常 -throw线程(在某些内部逻辑的基础上)ThreadAbortException(在这种情况下称为粗体线程中止)将被提升。尽管如此,我不知道Assert是否可以执行。

  4. 自定义主机可能已将升级策略应用于CLR( ICLRPolicyManager :: SetActionOnFailure )。在这种情况下,如果您处理OutOfMemoryException,升级策略可能会导致ThreadAbortException发生(再次粗体线程中止,不知道如果策略指示正常线程中止会发生什么情况)。

  5. 虽然@ Alois Kraus澄清说,正常线程中止异常是不可能的,从SSCLI研究我仍然怀疑(正常)ThreadAbortException可能发生。

  1. An InvalidProgramException can occur. This admittedly is highly highly improbable but nevertheless theoretically possible (for instance some internal CLR error may result in the throwable object becoming unavailable!!!!).
  2. If CLR does not find enough memory to process the 're-throw' action it will throw an OutOfMemoryException instead (CLR's internal re-throw logic requires to allocate some memory if it is not dealing with 'pre-allocated' exceptions like OutOfMemoryException).
  3. If the CLR is running under some other host (for e.g. SQL server or even your own) and the host decides to terminate the Exception re-throw thread (on the basis of some internal logic) ThreadAbortException (known as rude thread abort in this case) will be raised. Though, I am not sure if the Assert will even execute in this case.
  4. Custom host may have applied escalation policy to CLR (ICLRPolicyManager::SetActionOnFailure). In that case if you are dealing with an OutOfMemoryException, escalation policy may cause ThreadAbortException to occur (again rude thread abort. Not sure what happens if policy dictates a normal thread abort).
  5. Though @Alois Kraus clarifies that 'normal' thread abort exceptions are not possible, from SSCLI research I am still doubtful that (normal) ThreadAbortException can occur.

编辑:

如前所述,assert可以在理论上失败,但实际上这是非常不可能的。因此,为此开发POC是非常困难的。
为了提供更多的证据,以下是来自SSCLI代码的片段,用于处理 rethow IL指令,验证我的上述要点。

As I earlier said that the assert can fail theoretically but practically it is highly improbable. Hence it is very hard to develop a POC for this. In order to provide more 'evidence', following are the snippets from SSCLI code for processing rethow IL instruction which validate my above points.

警告:商业CLR可能与SSCLI差别很大。

Warning: Commercial CLR can differ very widely from SSCLI.


  1. InvalidProgramException:

  1. InvalidProgramException :

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


  • 粗体线程中止:

  • Rude Thread Abort :

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

    这意味着如果粗体线程中止已经启动,任何异常都会更改为粗线程中止异常。

    This means that if 'rude thread abort' has been initiated, any exception gets changed to rude thread abort exception.

    现在最有趣的是,$ code> OutOfMemoryException 。由于重命名IL指令本质上会重新抛出相同的Exception对象(即 object.ReferenceEquals 返回true),因此重复抛出OutOfMemoryException似乎是不可能的。但是,按照SSCLI代码显示可以:

    Now most interesting of all, the OutOfMemoryException. Since rethrow IL instruction essentially re-throws the same Exception object (i.e. object.ReferenceEquals returns true) it seems impossible that OutOfMemoryException can occur on re-throw. However, following SSCLI code shows that it is possible:

     // 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 (添加了我的评论)的代码段

    SafeSetLastThrownObject calls SetLastThrownObject and if it fails raises OutOfMemoryException. Here is the snippet from SetLastThrownObject (with my comments added)

    ...
    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句柄的非常罕见的情况。

    Above code snippets shows that the throwable object's GC handle is destroyed (i.e frees up a slot in GC table) and then a new handle is created. Since a slot has just been released, new handle creation will never fail until off-course in a highly rare scenario of a new thread getting scheduled just at the right time and consuming up all the available GC handles.

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

    Apart from this all exceptions (including rethrows) are raised through RaiseException win api. The code that catches this exception to prepare the corresponding managed exception can itself raise OutOfMemoryException.

    这篇关于C#中的(plain)throw语句可能会导致异常吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆