可以在C#原因例外(平原)throw语句? [英] Can the (plain) throw statement in C# cause exceptions?

查看:164
本文介绍了可以在C#原因例外(平原)throw语句?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问:能在平原在C#中的语句不断引起新的异常本身






请注意,我问这个问题出的的好奇心的,而不是因为我有任何实际或真实世界情况会很多事。另外请注意,我的直觉和经验告诉我,答案是否,但我正在寻找,以验证答案不知何故(见进一步下跌的源到目前为止,我已经试过)。



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

 
{
INT X = 0,Y = 1 / X;
}
赶上(例外outerException)
{


{
扔;
}
赶上(例外的InnerException)
{
//问:这是否曾经断言失败?
System.Diagnostics.Debug.Assert(outerException.Equals(的InnerException));
}
}



我不知道是否有在所有任何方式改变的情况下,这样的断言将失败,而不触及内心的try / catch 块。



我已经试过或正在寻找,试图回答这个问题:




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

  • 选中的 C#语言规范的一部分5.3.3.11 - 这是可能是错误的地方寻找这种信息的;

  • 通过我可以尝试触发throw语句异常掩盖。该OutOfMemoryException异常想到,却是一种难以在的

  • 开辟了ILDASM检查的时候触发生成的代码。我可以看到罚球将转换为重新抛出指令,但我失去了在哪里看得更远,以检查是否该声明可以或不可以抛出异常。



这是什么ILDASM展示了里面的尝试位:

 。尝试
{
IL_000d:NOP
IL_000e:重新抛出
} //结束。尝试

因此,要总结:可以throw语句(用重新抛出异常),不断的事业的异常本身呢?


解决方案

在我的真实想法,理论上。可以断言'失败'(实际上我不这么认为)



如何



请注意:以下。只是我的一些研究,我刚才做了SSCLI的基础上,意见




  1. 的的InvalidProgramException 。这诚然是非常非常难以琢磨但尽管如此,理论上是可能的(例如,可能会导致某些内部CLR错误的Throwable对象变得不可!!!!)。

  2. 如果CLR没有找到足够的内存来处理再抛的行动就会抛出,而不是一个OutOfMemoryException(CLR的内部重新掷逻辑要求分配一些内存,如果它不处理像OutOfMemoryException异常'预分配'例外)。

  3. 如果在CLR下其他主机运行(如SQL服务器甚至是你自己)和主机决定终止异常重-throw螺纹ThreadAbortException(一些内部逻辑的基础上)(称为粗鲁线程在这种情况下中止)将提高。虽然,我不知道,如果断言甚至会在这种情况下执行。

  4. 自定义主机可能已经申请升级政策CLR(的 ICLRPolicyManager :: SetActionOnFailure )。在这种情况下,如果你正在处理一个OutOfMemoryException,升级策略可能导致ThreadAbortException发生(再次粗鲁的线程中止。不知道政策规定正常线程终止时会发生什么)。

  5. 虽然@阿洛伊斯·克劳斯明确了正常线程中止的例外是不可能的,从SSCLI研究我仍然怀疑可能发生的(正常)ThreadAbortException。



编辑:



正如我刚才所说,该断言理论上可以失败,但实际上它是高度不可能的。因此,它是很难开发这个POC。
为了提供更多的证据,下面是来自SSCLI代码片段处理这验证我上述几点 rethow IL指令。

$ B 。商业CLR可以从SSCLI非常有很大的出入。




  1. InvalidProgramException:

    $ b

    警告>

     如果(抛出!= NULL)
    {

    }
    ,否则
    {
    //这只能是坏的IL(或一些内部EE失败)的结果。
    RealCOMPlusThrow(kInvalidProgramException,(UINT)IDS_EE_RETHROW_NOT_ALLOWED);
    }


  2. 粗鲁的线程中止:

     如果(pThread-> IsRudeAbortInitiated())
    {
    //没有人应该能够吞下粗鲁的线程中止。
    抛出= CLRException :: GetPreallocatedRudeThreadAbortException();
    }

    这意味着,如果粗鲁的线程中止已经启动,任何异常变改为粗鲁的线程中止异常。


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

      //始终保存当前对象的句柄等重新抛出,我们可以重用。这很重要,因为它
    //包含堆栈跟踪信息。
    //
    //注意:我们使用SafeSetLastThrownObject,这将尝试设置抛出,如果有任何问题,
    //它将设置抛出的东西appropiate(如OOM异常),并返回新的
    //例外。因此,用户的异常对象可以在这里所取代。

    抛出= pThread-> SafeSetLastThrownObject(抛出);



    SafeSetLastThrownObject 调用SetLastThrownObject如果失败加薪 OutOfMemoryException异常。下面是从 SetLastThrownObject 片段(添加了我的意见)

     。 .. 
    如果(m_LastThrownObjectHandle!= NULL)
    {
    //我们将somtimes使用手柄预先分配的异常对象。我们应该永远,永远消灭
    //这些手柄之一... ...他们会被破坏,当运行时关闭。 (!CLRException :: IsPreallocatedExceptionHandle(m_LastThrownObjectHandle))
    如果
    {
    //只而不是抛出对象本身
    DestroyHandle(m_LastThrownObjectHandle)销毁GC手柄;
    }
    }
    ...

    //如果没有留下一个新句柄
    m_LastThrownObjectHandle = GetDomain空间这一步可能失败() - > CreateHandle(抛出);



    上面的代码片段显示,Throwable对象的GC手柄被破坏(即释放了在GC表插槽),然后新的句柄被创建。由于槽刚刚发布,新手柄创作永远不会失败,直到在一个新的线程得到计划只是在合适的时间和消耗所有可用的GC手柄高度罕见的情景场外。




除了这所有的异常(包括重新抛出)通过的RaiseException 赢API。映入此异常制备相应的托管异常本身可以提高 OutOfMemoryException异常中的代码。


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

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:

  • 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.

This is what ILDASM shows for the inner try bit:

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

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

解决方案

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

How?

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

  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.

Edit:

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.

Warning: Commercial CLR can differ very widely from SSCLI.

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

  2. 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.

  3. 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 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);
    

    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.

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#原因例外(平原)throw语句?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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