【问题标题】:How to catch two exceptions in one stack when use using使用 using 时如何在一个堆栈中捕获两个异常
【发布时间】:2017-10-17 15:55:34
【问题描述】:
class Program
{
    static void Main(string[] args)
    {
        try
        {
            using (var ss = new extest()) {
                throw new Exception("Exception1");
            }
        }   
        catch(Exception ex)
        {
            System.Console.WriteLine(ex.Message);
        } 
    }


}

class extest : IDisposable
{
    public void Dispose()
    {
        throw new Exception("Exception2");
    }
}

运行代码结果为“Exception2”, 所以我想知道如何捕获两个异常,或者只捕获一个 Exception1。 我的项目有上千个这样的使用,不加try,但是extest的Dispose只有一个地方,希望能知道Dispose之前抛出了什么异常。

谢谢

【问题讨论】:

  • 我想了解你为什么要同时抓住这两个?您将如何根据其他例外情况改变您的行为。您似乎应该在 using 语句中移动 try catch 块。
  • 仅供参考,在Dispose 中抛出异常是个坏主意。但是要同时捕获两者,您需要在 using 中添加另一个 try-catch
  • 即使是异常过滤器 (stackoverflow.com/questions/4268223/c-sharp-exception-filter) 也看不到内部的 :(
  • 您拒绝以下答案,因为它们没有告诉您您想听什么,但事实是您想要做的是不可能。您可以访问待处理异常的唯一位置是在 catch 块内。在其他任何地方访问异常都需要在抛出异常或捕获异常时保存该异常。 C# 中没有 GetPendingException 方法,您可以随意调用它。所以你有两个潜在的解决方案,这两个都需要你改变你的using 块。抱歉,这就是你所在的位置。
  • 在我的项目中,业务逻辑在使用中,DataBase事务回滚在Dispose中。

标签: c# exception try-catch


【解决方案1】:

不可能捕获超过 1 个异常。

当你抛出 Exception2 时,它应该在你的 catch 子句中被捕获。当您看到“Exception2”时,它由System.Console.WriteLine(ex.Message); 打印。所以,你可以在 catch 中更改日志,或者在 Dispose 中更改抛出异常消息。

添加参考:

    class Program
{
    static void Main(string[] args)
    {
        try
        {
            using (var ss = new extest()) {
                ...
            }
        }   
        catch(Exception ex)
        {
            System.Console.WriteLine("extest error : " + ex.Message);
        } 
    }


}

class extest : IDisposable
{
    public void Dispose()
    {
        throw new Exception("Dispose failed: reason");
    }
}

【讨论】:

  • 如何在 Dispose 中捕获 Exception1
  • 您不需要在 Dispose 中捕获异常。它应该被抛出,调用它的函数应该捕获异常。
  • 如果不捕获多个异常,这种方法应该会更好
  • throw exception in Dispose() 告诉您在执行 Dispose 函数时出了什么问题,当您捕获到此异常时,您可以打印日志以显示异常发生的位置,并显示异常消息。这应该是处理异常的适当流程。
  • 不,如果使用中没有异常,但是Dispose没有抛出异常,下面的代码会继续
【解决方案2】:

处理此问题的最简单方法是重新排列您的代码:

static void Main(string[] args)
{
    try
    {
        using (var ss = new extest()) 
        {
           try
           {
                CodeThatMightThrowAnException();
           }
           catch (Exception e)
           {
               // Process Exception here
           }
        }
    }   
    catch(Exception ex)
    {
        System.Console.WriteLine(ex.Message);
    } 
}

编辑:

如果 using 中的异常处理总是相同的,您可以构建一个帮助类来简化重构:

public class TryCatchHelper
{
    public Exception Exception { get; private set; } = null;

    public void Execute(Action action)
    {
        try
        {
            action()
        }
        catch (Exception e)
        {
            exception = e;
        }
    }
}

然后在你的方法中:

static void Main(string[] args)
{
    var helper = new TryCatchHelper();
    try
    {            
        using (var ss = new extest()) 
        {
            helper.Execute(() => {
                // Your Code Block Here
            });              
        }
    }   
    catch(Exception ex)
    {
        // The Dispose threw an exception
    } 

    if (helper.Exception != null)
    {
        // Handle the exception from the block here.
    }   
}

【讨论】:

  • 我的项目有上千个这样的使用,不加try,但是extest的Dispose只有一个地方,希望能知道Dispose之前抛出了什么异常
  • 为什么extest的Dispose需要抛出异常?
  • 在我的项目中,业务逻辑在使用中,数据库事务回滚在Dispose中。
【解决方案3】:

您的示例中的问题是在处理第一个异常时引发了第二个异常。 IE。 using 语句实际上是 try/finally 对,在 finally 块中调用 Dispose()。所以,第二个例外取代了第一个。

拥有一个抛出异常的Dispose() 方法是一个非常糟糕的主意。所以,这里最好的解决方案是解决这个问题。不要从 Dispose() 方法中抛出异常。但是,如果由于某种原因您无法解决该问题并且您想同时查看两者,则需要确保您能够同时抓住两者。您可以通过在using 中添加另一个try/catch 来做到这一点:

try
{
    using (var ss = new extest()) {
        try
        {
            throw new Exception("Exception1");
        }
        catch (Exception exInner)
        {
            System.Console.WriteLine(ex.Message);
            throw;
        }
    }
}   
catch(Exception ex)
{
    System.Console.WriteLine(ex.Message);
}

【讨论】:

  • 我的项目有上千个这样的使用,不加try,但是extest的Dispose只有一个地方,希望知道Dispose之前抛出了什么异常
  • @xsharkx 在抛出异常之前没有将异常交给某个全局可访问的位置,Dispose 方法无法知道抛出的异常是什么。它甚至不知道为什么首先要处理它。
  • 参考规范说明为什么会出现这种行为:stackoverflow.com/questions/2911215/…
  • @AlexeiLevenkov 没用
  • @xsharkx: "我希望知道在 Dispose 之前抛出了什么异常" -- 为此,您必须在调用 Dispose() 之前捕获异常,如更多。如果抛出新异常,语言规范明确排除了对当前异常的任何了解。我要重申:这里真正的问题是您的 Dispose() 方法会引发异常。这是IDisposable 的行为不当、设计不正确的实现。来自 MSDN 上的长期建议:“Dispose 方法应该可以多次调用不会引发异常
猜你喜欢
  • 2014-04-23
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 2010-12-08
  • 2011-07-30
相关资源
最近更新 更多