简体   繁体   中英

Proper way to rethrow an exception

I have the following methods in c#:

void Method1()
{
    try
    {
        Method2();
    }
    catch(Method2Exception ex)
    {
        //Log error
    }
}

void Method2()
{
    if(error)
    {
        throw(new Method2Exception("error"));
    }

    //Do something and call method3
    try
    {
        Method3();
    }
    catch(Method3Exception)
    {
        //??
    }
}

void Method3()
{
    //Do something
    if(error)
    {
        throw(new Method3Exception("error"));
    }
}

Method3 its gonna be call by different methods and it returns Method3Exception and I need rethrow the exception from Method2 to Method1 but I don't want catch Method3Exception on Method1. what's the best way to do that?

Any suggestions

The term (re)throw usally refer to throwing the exception back to the caller preserving the stack trace (which contains where the exception exactly occurred). This can be done using throw; without specifying the exception operand contrary to throw ex :

try
{
    Method3();
}
catch(Method3Exception)
{
    throw;
}

However , if you're just going to add a throw with nothing before it in that method. It is useless, just remove the try..catch and the exception is going to propagate to the caller which is the default behavior.

Docs :

A throw statement can be used in a catch block to re-throw the exception that the catch block caught. In this case, the throw statement does not take an exception operand.

Alternative way to re-throwing the exception (using throw; as described in other answers) is to wrap the exception in inner exception. As described in MSDN , all custom exceptions should have at least four constructors, and one of them is

public InvalidDepartmentException(string message, System.Exception inner) : base(message, inner) { }

So if all your custom exceptions are like this, you could wrap the exception from Method3 as inner exception:

void Method2()
{
    if(error)
    {
        throw(new Method2Exception("error"));
    }

    //Do something and call method3
    try
    {
        Method3();
    }
    catch(Method3Exception exc)
    {
        throw new Method2Exception("error", exc); // exc is passed as inner exception
    }
}

Then if you want to inspect the inner exception in Method1 , you can use property InnerException :

void Method1()
{
    try
    {
        Method2();
    }
    catch(Method2Exception ex)
    {
        if(ex.InnerException != null)
        {
            var message = ex.InnerException.Message;
            // Do what you need with the message
        }
    }
}

In Method2 , you can throw a new Method2Exception with the existing Method3Exception as its InnerException :

try
{
    Method3();
}
catch(Method3Exception method3Exception)
{
    throw new Method2Exception("Message", method3Exception);
}

Then you can catch the Method2Exception above:

try
{
    Method2();
}
catch(Method2Exception ex)
{
    //Log error
}

Exceptions are bubbles up by default. For example,

void FirstMethod()
{
    int a = 0;
    int b = 10;
    int c = b / a;
}

void SecondMethod()
{
    FirstMethod();
}

void ThirdMethod()
{
    SecondMethod();
}

void FourthMethod()
{
    try
    {
        ThirdMethod();
    }
    catch (DivideByZeroException ex)
    {
        // Handle error
    } 
}

The exception will occur in FirstMethod and it will go upwards and will be cought at ForurthMethod . If you want to log the exception at ThirdMethod for example, but still want your exception to be handled at FourthMethod then you have to options:

First option:

void ThirdMethod()
{
    try
    {
        SecondMethod();
    }
    catch (Exception ex)
    {
        // Log the error
        throw; // Throw exception without affecting StackTrace
    }   
}

Second option:

After C# 6.0 you can do this with ease by using exception filters. Create a logger method which returns false .

bool Log(Exception ex)
{
    // Log the error
    return false;
}

Add exception filter in third method:

void ThirdMethod()
{
    try
    {
        SecondMethod();
    }
    catch (Exception ex) when(Log(ex))
    {
        // Debugger won't reach here
    }   
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM