C# 为什么ExecuteCodeWithGuarantedCleanup不';不行?

C# 为什么ExecuteCodeWithGuarantedCleanup不';不行?,c#,C#,我试图“测量”堆栈深度。为什么下面的程序不打印任何内容 class Program { private static int Depth = 0; static void A(object o) { Depth++; A(o); } static void B(object o, bool e) { Console.WriteLine(Depth); } static void

我试图“测量”堆栈深度。为什么下面的程序不打印任何内容

class Program
{
    private static int Depth = 0;

    static void A(object o) 
    {
        Depth++;
        A(o);
    }

    static void B(object o, bool e)
    {
        Console.WriteLine(Depth);
    }

    static void Main(string[] args)
    {
        RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(A, B, null);
    }
}
一些答案只是引用了MSDN的一句话,比如“从.NET Framework 2.0版开始,try-catch块无法捕获StackOverflowException对象,并且默认情况下会终止相应的进程。”相信我,有时(当堆栈空间足够时)可以,下面打印的一些数字很好:

class Program
{
    private static int depth = 0;

    static void A(object o)
    {
        depth++;
        if (Environment.StackTrace.Length > 8000)
            throw new StackOverflowException("Catch me if you can.");
        A(o);
    }

    static void B(object o, bool e)
    {
        Console.WriteLine(depth);
    }

    static void Main(string[] args)
    {
        RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(A, B, null);
    }
}
根据:

在.NET的早期版本中 框架,您的应用程序可以 捕获StackOverflowException对象 (例如,从 无界递归)。但是, 目前不鼓励这种做法 因为重要的附加代码是 需要可靠地捕获堆栈 溢出异常并继续 程序执行


从.NET框架开始 版本2.0,StackOverflowException 对象不能被try-catch捕获 块,相应的进程是 默认终止。因此,, 建议用户编写自己的代码 检测并防止堆栈 溢流例如,如果您的 应用程序依赖于递归,请使用 计数器或状态条件 终止递归循环。注 这是一个承载 公共语言运行库(CLR)可以 指定CLR卸载 堆栈所在的应用程序域 发生溢出异常并让 相应的过程继续。对于 有关详细信息,请参阅 ICLRPolicyManager接口和 托管公共语言运行库


由于这一事实,我不相信您能够做您想做的事情,因为“StackOverflowException”将终止该进程。

从.NET Framework 2.0版开始,一个StackOverflowException对象不能被try-catch块捕获,如果您想捕获它,默认情况下会终止相应的进程,将其加载到另一个进程(通过远程处理调用您的进程)中,并让恶意代码在那里执行。另一个过程可能会终止,您可能会得到一个整洁的SOE从您这边的管道末端弹出——而不会受到相当不方便的异常的不利影响

请注意,在同一进程中使用单独的AppDomain不会剪切它

如果您想从异常中获取堆栈跟踪,以下代码将为您带来极大好处:

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Recurse(0);
            }
            catch (Exception ex)
            {
                StackTrace st = new StackTrace(ex);
                // Go wild.
                Console.WriteLine(st.FrameCount);
            }
            Console.ReadLine();
        }

        static void Recurse(int counter)
        {
            if (counter >= 100)
                throw new Exception();
            Recurse(++counter);
        }
    }

关于您的编辑,我认为抛出StackOverflowException的用户代码与抛出它的CLR不同

关于这件事有一些讨论

Jeffrey(Richter,作者)的评论适用于真正的堆栈溢出,即堆栈溢出 如果代码包含无限递归,则会发生这种情况,例如:

void MyMethod(){MyMethod();}

如果你扔 StackOverflowException你自己,它 将像其他任何人一样处理 例外,杰弗里的评论确实如此 不适用

此外,杰弗里的评论说:“如果 CLR中发生堆栈溢出 本身……”。因此,如果.NET虚拟机可以 “干净地”检测堆栈溢出, i、 不让自己陷入困境 堆栈溢出,那么您应该得到一个 StackOverflowException和您的捕获 最后,块应按如下方式执行 平常的。但是,在悲惨的情况下 虚拟机本身运行在一个堆栈中 溢出,你就不会那么幸运了 VM不会传播 StackOverflowException(但在中崩溃) 其他一些奇怪的方式)和你的捕获 最后,块将不会执行

士气是:小心无限 递归,因为您没有 100%保证虚拟机将检测到 清楚地向他们发出信号

布鲁诺


澄清了谁是“杰弗里”,因为OP引用了那本书。

+1。这很酷,伙计。您还可以就如何获取深度值提供建议吗。。。在堆栈跟踪中添加了一些内容。请注意,StackTrace位于System.Diagnostics中,是一个较慢的生物(因此请明智地使用它)。关于这段10年前的旧代码,请注意:它不测量堆栈深度,而是测量嵌套调用的数量。因为它不是尾部递归的,所以该部分可以工作,但堆栈深度(从堆栈中取出的字节数)取决于局部变量的数量、它们是否为结构、函数的参数数量以及调用的函数是否由JIT内联。另外,获取stacktrace和framecount非常昂贵。而且您有一个支持此声明的引用吗?顺便说一句,ExecuteCodeWithGuaranteedClenup甚至不使用try/catch。它使用结构化异常处理(SEH)。@prankster,即使它确实抛出与用户代码相同的异常,SOE(或OutOfMemoryException,另一个问题ex)也会使堆栈处于损坏状态,运行时可能无法修复它。。。这就是为什么他们更改了它,因为1.0可能处理得不可靠。不确定第二个链接为什么会断开。在预览中看起来不错。第二个链接只是指向书的链接。“相信我,有时候(当有足够的堆栈空间时)它会被咳嗽”;的确当您手动实例化一个
StackOverflowException
时,它不会被视为一个关键异常(CSE:Corrude State exception),它的工作方式与任何其他异常一样。同样,如果你用手投掷任何其他CSE。更多信息: