C# try..catch - redirecting error handling flow from one catch to the next
Asked Answered
T

7

6

I have a try..catch block that looks like this:

try
{
    ...
}
catch (IOException ioEx)
{
    ...
}
catch (Exception ex)
{
    ... 
}

I'd like to handle just a certain kind of IOException, namely a sharing violation (Win32 0x20). Other IOExceptions and all other Exception descendants should be handled generally by the second catch-all catch.

Once I know that the IOException is not a sharing violation, how can I cleanly redirect the error handling flow to the general catch? If I rethrow in catch (IOException) the second catch does not invoke. I know I can nest try..catches but is there a cleaner way?

EDIT: On factoring-out handler logic

Factoring repeated code in methods will surely work, but I noticed that in general when you use factored methods for exception handling it tends to have subtle problems.

First of all, a catch clause has direct access to all of the local variables prior to the exception. But when you "outsource" exception handling to a different method then you have to pass the state to it. And when you change the code so does the handler method's signature changes, which might be a maintainability issue in more complicated scenarios.

The other problem is that program flow might be obscured. For example, if the handler method eventually rethrows the exception, the C# compiler and code analyzers like Resharper don't see it:

    private void Foo()
    {
        string a = null;

        try
        {
            a = Path.GetDirectoryName(a);
            System.Diagnostics.Debug.Print(a);
        }
        catch (Exception ex)
        {                
            HandleException(ex, a); //Note that we have to pass the "a"
            System.Diagnostics.Debug.Print(
                "We never get here and it's not obvious" + 
                "until you read and understand HandleException"
            );
            ...!
        }
    }

    static void HandleException(Exception ex, string a)
    {
        if (a != null)
            System.Diagnostics.Debug.Print("[a] was not null");
        throw (ex); //Rethrow so that the application-level handler catches and logs it
    }

VS

    private void Bar()
    {
        string a = null;

        try
        {
            a = System.IO.Path.GetDirectoryName(a);
            System.Diagnostics.Debug.Print(a);
        }
        catch (Exception ex)
        {                
            if (a != null)
                System.Diagnostics.Debug.Print("[a] was not null");
            throw; //Rethrow so that the application-level handler catches and logs it
            System.Diagnostics.Debug.Print(
                "We never get here also, but now " + 
                "it's obvious and the compiler complains"
            );
            ...!
        }
    }

If I want to avoid these kind of (minor) problems then it seems that there is no cleaner way than nesting try..catch blocks, as Hank pointed out.

Tacye answered 27/5, 2011 at 9:17 Comment(1)
It feels like the right way to do this is to have a different exception for your sharing violation but that does of course then basically come down to your nested catches. I don't think there is much you can do off the top of my head but I look forward to seeing if I am wrong. :)Jurat
T
-1

C# 6 introduced the when exception filter that enables better control of which catch block should be invoked. The when accepts an expression that has access to the exception object and returns a bool. If the expression yields false then the handler is skipped an the next handler is considered:

        try
        {
            var responseText = await streamTask;
            return responseText;
        }
        catch (HttpRequestException e) when (e.Message.Contains("301"))
        {
            return "Site Moved";
        }
        catch (HttpRequestException e) when (e.Message.Contains("404"))
        {
            return "Page Not Found";
        }
        catch (HttpRequestException e)
        {
            return e.Message;
        }
Tacye answered 29/2, 2024 at 15:59 Comment(0)
R
4

Just factor the handling logic into a separate method.

try
{
    ...
}
catch (IOException ioEx)
{
    if (sharing violation)
       HandleSharingViolation();
    else 
       HandleNonsharingViolation();
}
catch (Exception ex)
{
       HandleNonsharingViolation();
}

Or test the exceptions yourself

catch (Exception ex)
{
     if (ex is IOException && ex.IsSharingViolation()
       HandleSharingViolation();
     else
       HandleNonsharingViolation();
}
Reliance answered 27/5, 2011 at 9:22 Comment(0)
E
3

No, you'll have to nest.

Once you are in 1 of the catch blocks, this 'try' is considered handled.

And I think it may make a lot of sense, "sharing violation" sounds like a special case that probably isn't so tightly coupled to the rest as you might be thinking. If you use nest try-catch, does the try block of the special case has to surround the exact same code? And of course it's a candidate to refactor out as a separate method.

Exosmosis answered 27/5, 2011 at 9:21 Comment(0)
O
0

Create Method to handle exception, pass the exception to that method , based on the type Handle the exception in the way you want.Call these method in both these blocks.

Ology answered 27/5, 2011 at 9:20 Comment(0)
L
0

Use nested try catch blocks.

try
{
    try
    {
    }
    catch (IOException ioEx)
    {
        if (....)
        else
           throw;
    }
}
catch
{
}
Lituus answered 27/5, 2011 at 9:21 Comment(0)
B
0

what about "finally"?

you can first set a 'variable' in the IOException block once you know the IOException is not sharing violation. Then, in your finally block, if that 'variable' is set, you proceed to do whatever you need to do.

Below impl. tested and confirmed.

        bool booleanValue = false;
        try
        {
            test1(); // this would thro IOException

        }
        catch (IOException e)
        {
            booleanValue = true; // whatever you need to do next
        }
        finally
        {
            if (booleanValue)
            {
                Console.WriteLine("Here");
            }
        }
Bharat answered 27/5, 2011 at 9:24 Comment(1)
tested. bool booleanValue = false; try { test1(); // this would thro IOException } catch (IOException e) { booleanValue = true; // whatever you need to do next } finally { if (booleanValue) { Console.WriteLine("Here"); } }Bharat
S
0

Tryout this nested block

try
{

}

catch(Exception ioex)
{
try
{

}
catch(Exception ex)
{

}
}

Slesvig answered 27/5, 2011 at 9:31 Comment(0)
T
-1

C# 6 introduced the when exception filter that enables better control of which catch block should be invoked. The when accepts an expression that has access to the exception object and returns a bool. If the expression yields false then the handler is skipped an the next handler is considered:

        try
        {
            var responseText = await streamTask;
            return responseText;
        }
        catch (HttpRequestException e) when (e.Message.Contains("301"))
        {
            return "Site Moved";
        }
        catch (HttpRequestException e) when (e.Message.Contains("404"))
        {
            return "Page Not Found";
        }
        catch (HttpRequestException e)
        {
            return e.Message;
        }
Tacye answered 29/2, 2024 at 15:59 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.