【问题标题】:C# - Infinite Loop at Exception Throwing?C# - 抛出异常时的无限循环?
【发布时间】:2014-07-26 02:36:22
【问题描述】:

我有以下代码:

protected void ExecuteInTransaction(Action action)
{
    using (SQLiteTransaction transaction = connection.BeginTransaction())
    {
        try
        {
            action.Invoke();
            transaction.Commit();
        }
        catch(Exception)
        {
            transaction.Rollback();
            throw;
        }
    }
}

在测试这个类时,我设法抛出了一个异常来测试catch 分支

由于我处于调试模式,我继续执行这个抛出,以查看调用类如何处理它,但从不例外方法抛出,相反,它就像异常不断地被抛出和捕获,再次抛出和捕获,永远不会退出函数

Release模式下,应用程序冻结并停止工作:

有人知道为什么会发生这种情况,我该如何避免?

提前致谢!

【问题讨论】:

  • 捕获 SQL 特定异常并打印出消息并将回滚包装在 try catch 中

标签: c# .net sqlite exception exception-handling


【解决方案1】:

没有无限循环。 Visual Studio 只是停在未捕获的异常会中止程序的地方。 尝试继续没有任何作用,因为没有进一步的执行(VS 只是再次显示相同的消息以提醒您)。

如果你在某个调用函数中有一个 try/catch 处理程序,你就可以在那里调试。 (但如果那个 catch 处理程序再次抛出,VS 也会停在那里。)


请注意SQLiteTransaction 在处理打开的事务时会自动处理回滚;它旨在让您的代码更简单:

protected void ExecuteInTransaction(Action action)
{
    using (var transaction = connection.BeginTransaction())
    {
        action.Invoke();
        transaction.Commit();
    }
}

【讨论】:

  • 哇,不知道SQLiteTransaction 的这种行为,非常感谢!看来您对未捕获的异常是正确的,看来我认为我是从更高的级别捕获它,但我真的不是。谢谢大家!
【解决方案2】:

您确定堆栈中有一个catch 可以处理此错误吗?您显示的对话框是您在程序的 Main 方法顶部未处理异常时看到的内容。调试器消息实际上告诉您它未处理,因此没有下一步要执行的语句。

【讨论】:

    【解决方案3】:

    有人知道为什么会发生这种情况,我该如何避免?

    没有看到你的调用堆栈很难说。

    一般有3种可能的选择:

    1. 异常被捕获到更高的堆栈。

    2. 在您的调用堆栈中某处存在内核模式系统调用,异常被吞没。只有在 64 位窗口上运行 32 位应用程序时才会发生这种情况。最值得注意的例子是FormOnLoad() 方法中抛出的异常。请参阅VS2010 does not show unhandled exception message in a WinForms Application on a 64-bit version of Windows 了解更多信息。

    3. ThreadPool 线程上抛出异常,而不是传播回主线程​​。

    【讨论】:

    • 我注意到#3 仅与 .NET 版本 1.X 相关。但仍有可能异常被等待回调中的 try/catch 捕获,而该异常可能不会出现在调用堆栈中。
    【解决方案4】:

    throw; 代码从catch 块中取出。如果您想知道代码何时进入 catch 块,请使用断点或Debug.WriteLine()

    try/catch 的 catch 块不会捕获自身抛出的异常。所以throw; 代码正在创建一个未处理的异常。如果您想测试 catch 块中的代码,请将 throw; 代码添加到 try 块的末尾。

    编辑: 我没有意识到 OP 希望例外来传播链条。他没有提到异常正在向上传播,他的代码不支持向上传播的异常,因为他没有显示调用此ExecuteInTransaction(Action) 方法的代码。 catch 块可以重新抛出它捕获的异常。我同意这一点。但是代码catch(Exception){ throw; } 不会重新进入同一个catch 块。如果它会创建一个无限循环,那不会发生。如果周围有一个 try/catch 块,则外部 catch 块将捕获重新抛出的异常,但他的代码仅包含一个 catch 块。因此,当它尝试重新抛出异常时,没有任何东西可以捕获它并且应用程序会中断。

    试试这样的:

    private void Main()
    {
        // Instantiate action variable. I know this wouldn't work, but it's just for show.
        Action myAction;
        try
        {
            ExecuteInTransaction(myAction);
        }
        catch(Exception ex)
        {
            Debug.WriteLine("Error happened and transaction rolled back. " + ex.Message);
        }
    }
    
    protected void ExecuteInTransaction(Action action)
    {
        using (SQLiteTransaction transaction = connection.BeginTransaction())
        {
            try
            {
                action.Invoke();
                transaction.Commit();
            }
            catch(Exception ex)
            {
                transaction.Rollback();
                throw ex;
            }
        }
    }
    

    【讨论】:

    • 他不想吞下异常;这不会使程序正常运行。他希望回滚事务,然后让异常继续传播。您断言 catch 块不会捕获它重新抛出的异常是错误的。它确实抓住了它。它在做某事后重新抛出它(在这种情况下回滚事务),但它确实捕获了它。
    • 这正是我使用throw@Servy 的原因,我无法更好地解释它
    • @MatiCicero 尊敬的,我相信它可以解释得更好一点。我刚刚编辑了我的答案。如果我仍然不明白你想做什么,请告诉我。
    猜你喜欢
    • 2013-03-17
    • 2016-05-30
    • 1970-01-01
    • 1970-01-01
    • 2014-04-03
    • 1970-01-01
    • 2013-06-23
    • 1970-01-01
    • 2013-09-28
    相关资源
    最近更新 更多