46

This question is intended to apply to any OO programming language that supports exception handling; I am using C# for illustrative purposes only.

Exceptions are usually intended to be raised when an problem arises that the code cannot immediately handle, and then to be caught in a catch clause in a different location (usually an outer stack frame).

Q: Are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?

This question came up for me because .NET 4's System.IObserver<T>.OnError method suggests just that: exceptions being passed around as error objects.

Let's look at another scenario, validation. Let's say I am following conventional wisdom, and that I am therefore distinguishing between an error object type IValidationError and a separate exception type ValidationException that is used to report unexpected errors:

partial interface IValidationError { }

abstract partial class ValidationException : System.Exception
{
    public abstract IValidationError[] ValidationErrors { get; }
}

(The System.Component.DataAnnotations namespace does something quite similar.)

These types could be employed as follows:

partial interface IFoo { }  // an immutable type

partial interface IFooBuilder  // mutable counterpart to prepare instances of above type
{
    bool IsValid(out IValidationError[] validationErrors);  // true if no validation error occurs
    IFoo Build();  // throws ValidationException if !IsValid(…)
}

Now I am wondering, could I not simplify the above to this:

partial class ValidationError : System.Exception { }  // = IValidationError + ValidationException

partial interface IFoo { }  // (unchanged)

partial interface IFooBuilder
{
    bool IsValid(out ValidationError[] validationErrors);
    IFoo Build();  // may throw ValidationError or sth. like AggregateException<ValidationError>
}

Q: What are the advantages and disadvantages of these two differing approaches?

stakx
  • 2,128

8 Answers8

51

Returning exceptions instead of throwing them can make semantical sense when you have a helper-method for analyzing the situation and returning an appropriate exception which is then thrown by the caller (you could call this an "exception factory"). Throwing an exception in this error analyzer function would mean that something went wrong during the analysis itself, while returning an exception means that the kind of error was analyzed succesfully.

One possible use-case could be a function which turns HTTP response codes into exceptions:

Exception analyzeHttpError(int errorCode) {
    if (errorCode < 400) {
         throw new NotAnErrorException();
    }
    switch (errorCode) {
        case 403:
             return new ForbiddenException();
        case 404:
             return new NotFoundException();
        case 500:
             return new InternalServerErrorException();
        …
        default:
             throw new UnknownHttpErrorCodeException(errorCode);
     }
}

Note that throwing an exception means that the method was used wrong or had an internal error, while returning an exception means that the error code was identified succesfully.

stakx
  • 2,128
Philipp
  • 23,488
31

Are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?

If it is never thrown then it's not an exception. It is an object derived from an Exception class, though it does not follow the behavior. Calling it an Exception is purely semantics at this point, but I see nothing wrong with not throwing it. From my perspective, not throwing an exception is the exact same thing as an inner-function catching it and preventing it from propagating.

Is it valid for a function to return exception objects?

Absolutely. Here is a short list of examples where this may be appropriate:

  • An exception factory.
  • A context object that reports if there was a previous error as a ready to use exception.
  • A function that keeps a previously caught exception.
  • A third-party API that creates an exception of an inner type.

Is not throwing it bad?

Throwing an exception is kind of like: "Go to jail. Do not pass Go!" in the board game Monopoly. It tells a compiler to jump over all source code up to the catch without executing any of that source code. It doesn't have anything to do with errors, reporting or stopping bugs. I like to think of throwing an exception as a "super return" statement for a function. It returns execution of the code to somewhere much higher up than the original caller.

The important thing here is to understand that the true value of exceptions is in the try/catch pattern, and not the instantiation of the exception object. The exception object is just a state message.

In your question you seem to be confusing the usage of these two things: a jumping to a handler of the exception, and the error state the exception represents. Just because you took an error state and wrapped it in an exception does not mean you are following the try/catch pattern or its benefits.

Reactgular
  • 13,120
  • 4
  • 50
  • 81
5

Conceptually, if the exception object is the expected result of the operation, then yes. The cases I can think of, however, always involve throw-catch at some point:

  • Variations on the "Try" pattern (where a method encapsulates a second exception-throwing method, but catches the exception and instead returns a boolean indicating success). You could, instead of returning a Boolean, return any Exception thrown (null if it succeeded), allowing the end user more information while retaining the catch-less success or failure indication.

  • Workflow error processing. Similar in practice to "try pattern" method encapsulation, you might have a workflow step abstracted in a chain-of-command pattern. If a link in the chain throws an exception, it's often cleaner to catch that exception in the abstracting object, then return it as a result of said operation so the workflow engine and the actual code run as a workflow step don't require extensive try-catch logic of their own.

KeithS
  • 22,282
5

Lets say you enqued some task to be executed in some thread pool. If this task throw exception, it is different thread so you don't catch it. Thread where it was executed just die.

Now consider that something (either code in that task, or the thread pool implementation) catches that exception store it along with task and consider task finished (unsucessfully). Now you can ask whether task is finished (and either ask whether it threw, or it can be thrown again in your thread (or better, new exception with original as cause)).

If you do it manually, you can notice that you are creating new exception, throwing it and catching it, then storing it, in different thread retrieving throwing, catching and reacting on it. So it can make sense to skip throw and catch and just store it and finish task and then just ask whether there is exception and react on it. But this can lead in more complicated code if in the same place there can be really thrown exceptions.

PS: this is written with experience with Java, where stack trace information is created when exception is created, (unlike C# where it is created when throwing). So not thrown exception approach in Java will be slower than in C# (unless its precreated and reused), but will have stack trace info available.

In general, I would stay away from creating exception and never throwing it (unless as performance optimalization after profiling point it out as bottleneck). At least in java, where exception creation is expensive (stack trace). In C# it is possibility, but IMO its surprising and thus should be avoided.

user470365
  • 1,259
1

It depends on your design, typically I would not return exceptions to a caller, rather I would throw and catch them and leave it at that. Typically code is written to fail early and fast. For example consider the case of opening a file and processing it (This is C# PsuedoCode):

        private static void ProcessFileFailFast()
        {
            try
            {
                using (var file = new System.IO.StreamReader("c:\\test.txt"))
                {
                    string line;
                    while ((line = file.ReadLine()) != null)
                    {
                        ProcessLine(line);
                    }
                }
            }
            catch (Exception ex) 
            {
                LogException(ex);
            }
        }

        private static void ProcessLine(string line)
        {
            //TODO:  Process Line
        }

        private static void LogException(Exception ex)
        {
            //TODO:  Log Exception
        }

In this case, we would error out and stop processing the file as soon as it encountered a bad record.

But say the requirement is we want to continue processing the file even if one or more lines has an error. The code may start to look something like this:

    private static void ProcessFileFailAndContinue()
    {
        try
        {
            using (var file = new System.IO.StreamReader("c:\\test.txt"))
            {
                string line;
                while ((line = file.ReadLine()) != null)
                {
                    Exception ex = ProcessLineReturnException(line);
                    if (ex != null)
                    {
                        _Errors.Add(ex);
                    }
                }
            }

            //Do something with _Errors Here
        }
        //Catch errors specifically around opening the file
        catch (System.IO.FileNotFoundException fnfe) 
        { 
            LogException(fnfe);
        }    
    }

    private static Exception ProcessLineReturnException(string line)
    {
        try
        {
            //TODO:  Process Line
        }
        catch (Exception ex)
        {
            LogException(ex);
            return ex;
        }

        return null;
    }

So in this case we return the exception back to the caller, although I would probably not return an exception back but instead some sort of error object since the exception has been caught and already dealt with. This is no harm with returning an exception back but other callers could re-throw the exception which may not be desireable since exception object have that behavior. If you wanted callers have the ability to re-throw then return an exception, otherwise, take the information out of the exception object and construct a smaller light weight object and return that instead. The fail fast is usually cleaner code.

For your validation example, I probably wouldn't inherit from an exception class or throw exceptions because validation errors could be quite common. It would be less overhead to return an object rather than to throw an exception if 50% of my users fail to fill out a form correctly on the first attempt.

Jon Raynor
  • 11,773
1

Q: Are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?

Yes. For example, I recently encountered a situation situation where I found that thrown Exceptions in a ASMX Web Service donot include a element in the resultant SOAP message, so I had to generate it.

Illustrative code:

Public Sub SomeWebMethod()
    Try
        ...
    Catch ex As Exception
        Dim soapex As SoapException = Me.GenerateSoapFault(ex)
        Throw soapex
    End Try
End Sub

Private Function GenerateSoapFault(ex As Exception) As SoapException
    Dim document As XmlDocument = New XmlDocument()
    Dim faultDetails As XmlNode = document.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace)
    faultDetails.InnerText = ex.Message
    Dim exceptionType As String = ex.GetType().ToString()
    Dim soapex As SoapException = New SoapException("SoapException", SoapException.ClientFaultCode, Context.Request.Url.ToString, faultDetails)
    Return soapex
End Function
Tom Tom
  • 419
1

In most languages (afaik), exceptions come with some added goodies. Mostly, a snapshot of the current stack trace is stored in the object. This is nice for debugging, but can also have memory implications.

As @ThinkingMedia already said, you are really using the exception as an error object.

In your code example, it seems you mainly do this for code reuse and to avoid composition. I personally don't think this is a good reason to do this.

Another possible reason would be to trick the language to give you an error object with a stack trace. This gives additional contextual information to your error handling code.

On the other hand, we can assume that keeping the stack trace around has a memory cost. E.g. if you start collecting these objects somewhere, you may see an unpleasant memory impact. Of course this largely depends on how exception stack traces are implemented in the respective language / engine.

So, is it a good idea?

So far I have not seen it being used or recommended. But this does not mean much.

The question is: Is the stack trace really useful for your error handling? Or more generally, which information do you want to provide to the developer or administrator?

The typical throw/try/catch pattern makes it kind of necessary to have a stack trace, because otherwise you have no clue where it is coming from. For a returned object, it always comes from the called function. The stack trace might contain all the information that the developer needs, but maybe something less heavy and more specific would suffice.

-4

The answer is yes. See http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions and open up the arrow for Google's C++ style guide on exceptions. You can see there the arguments that they used to decide against, but say that if they had to do it over again, they would have likely decided for.

However it is worth noting that the Go language does not idiomatically use exceptions either, for similar reasons.

btilly
  • 18,340