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

Run the codes result is "Exception2", So I want to know how you can catch two exceptions, or just catch an Exception1. My project has thousands of such using, which does not add try, but extest's Dispose is only one place, and I hope to know what exception has thrown before the Dispose.

Thanks

xsharkx
  • 19
  • 4
  • 1
    I am trying to understand why you would want to catch both? How would you change your behavior based on the other exceptions. It seems to be that you should be moving the try catch block inside your using statement. – mageos May 18 '17 at 02:26
  • 1
    FYI, throwing exceptions in `Dispose` is a bad idea. But to catch both you'd put another `try-catch` inside of the `using`. – juharr May 18 '17 at 02:34
  • Even exception filters (http://stackoverflow.com/questions/4268223/c-sharp-exception-filter) can't see inner one :( – Alexei Levenkov May 18 '17 at 03:02
  • You are rejecting the below answers because they don't tell you what you want to hear, but the fact of the matter is what you are wanting to do is _impossible_. The only place where you can access the pending exception is within a `catch` block. Accessing the exception anywhere else requires that you save that exception either as you throw it or when you catch it. There is no `GetPendingException` method in C# that you can just call somewhere arbitrarily. So you have two potential solutions, both of which require that you change your `using` block. Sorry, but that's just where you are. – Abion47 May 18 '17 at 03:24
  • In my project,Business logic is In using,DataBase transaction rollback is in Dispose. – xsharkx May 18 '17 at 03:50
  • _"DataBase transaction rollback is in Dispose"_ -- you should be more specific. Why is it that every time the object is disposed, you want to rollback? Shouldn't rollback happen only on some failure or cancellation? Are you sure `IDisposable.Dispose()` is really the best place for logic that can fail? That's certainly against every guideline ever written regarding how to implement `IDisposable.Dispose()`. – Peter Duniho May 18 '17 at 04:03

3 Answers3

2

The problem in your example is that the second exception is thrown while the first exception is being handled. I.e. the using statement is effectively a try/finally pair, with the call to Dispose() in the finally block. So, the second exception supersedes the first one.

Having a Dispose() method that throws an exception is a very bad idea. So, the best solution here is to fix that. Don't throw an exception from a Dispose() method. But if you can't fix that for some reason and you want to see both, you need to make sure you're in a position to catch both. You can do this by adding another try/catch inside the using:

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);
}
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • My project has thousands of such using, which does not add try, but extest's Dispose is only one place, and I hope to know what exception has thrown before the Dispose – xsharkx May 18 '17 at 02:38
  • 1
    @xsharkx Short of handing the exception to some globally accessible location before throwing it, there's no way for the `Dispose` method to know what the exception was that was thrown. It doesn't even know why it's getting disposed in the first place. – Abion47 May 18 '17 at 02:46
  • Reference to spec on why this behavior is expected: http://stackoverflow.com/questions/2911215/what-happens-if-a-finally-block-throws-an-exception – Alexei Levenkov May 18 '17 at 02:54
  • @AlexeiLevenkov not useful – xsharkx May 18 '17 at 03:27
  • @xsharkx: _"I hope to know what exception has thrown before the Dispose"_ -- to do that, you have to catch the exception before calling `Dispose()`, as above. The language specification specifically precludes knowing anything about the current exception if a new one is thrown. I will reiterate: the real problem here is that your `Dispose()` method throws an exception. That's a badly behaved, incorrectly designed implementation of `IDisposable`. From the long-standing advice on MSDN: _"a Dispose method should be callable multiple times **without throwing an exception**"_ – Peter Duniho May 18 '17 at 03:54
  • @xsharkx: see, for example: http://stackoverflow.com/questions/577607/should-you-implement-idisposable-dispose-so-that-it-never-throws, http://stackoverflow.com/questions/1030455/how-to-handle-exception-thrown-from-dispose, http://stackoverflow.com/questions/2323116/how-should-i-handle-exceptions-in-my-dispose-method, http://stackoverflow.com/questions/19238521/handling-exceptions-thrown-by-dispose-while-unwinding-nested-using-statement, https://blogs.msdn.microsoft.com/clyon/2004/09/23/dispose-dos-and-donts/, and https://msdn.microsoft.com/en-us/library/bb386039.aspx – Peter Duniho May 18 '17 at 03:59
0

The easiest way to handle this would be to rearrange your code:

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

Edit:

If the handling of the exceptions inside the using is always going to be the same, you could build a helper class that could make refactoring easier:

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

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

Then in your method:

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.
    }   
}
mageos
  • 1,216
  • 7
  • 15
  • My project has thousands of such using, which does not add try, but extest's Dispose is only one place, and I hope to know what exception has thrown before the Dispose – xsharkx May 18 '17 at 02:38
  • Why does extest's Dispose need to throw an exception? – mageos May 18 '17 at 02:42
  • In my project,Business logic is In using,DataBase transaction rollback is in Dispose. – xsharkx May 18 '17 at 03:30
-1

it's impossible to catch more than 1 exception.

when you throw Exception2 it should be catched in your catch clause. when you see "Exception2" it is printed by System.Console.WriteLine(ex.Message);. So, you can change the log in catch, or change the throwing exception message in Dispose.

reference added:

    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");
    }
}
Zedee.Chen
  • 212
  • 1
  • 7
  • How can i catch Exception1 in Dispose – xsharkx May 18 '17 at 02:29
  • you don't need to catch exception in Dispose. it should be throw out and the function which invoke it should catch the exception. – Zedee.Chen May 18 '17 at 02:33
  • This approach should be better if you do not catch multiple exceptions – xsharkx May 18 '17 at 02:49
  • 1
    throw exception in Dispose() tells what's wrong when executing Dispose function, and when you catch this exception, you can print a log to show where the exception happens, and also show the exception message. that should be a proper flow to deal with an exception. – Zedee.Chen May 18 '17 at 02:54
  • No, if there is no exception in using, but Dispose does not throw an exception, the following code will continue – xsharkx May 18 '17 at 03:10
  • I need throw exception in "Dispose" when no exception in using. – xsharkx May 18 '17 at 03:32
  • @xsharkx You can't do that without changing the code in the `using`. There's no other way for `Dispose` to know whether or not an exception was thrown. – Abion47 May 18 '17 at 03:40
  • It's a odd demand to force exception while running function. an exception should only be thrown while some condition is met, for example, in a if clause. you can implement that with other way, like `return`. – Zedee.Chen May 18 '17 at 03:45