-1

Can someone please explain the differences in C# between:

  • throw
  • throw new

and exactly how exceptions "bubble up" as I've heard they do?


In my daily job, I've used just try/catch to mostly control the flow. I've divided the code into various "blocks" each one enclosed in a try/catch so I could log and debug exactly where a program crashed/had a problem, if there was any.

In researching, I've found there is a "bubble" logic behind the exception - which is used in more structured software development. For example, if I have a system in which I build modules/plugins/complex complementary classes, I might want the exceptions to point me to exactly where the code failed, OR, bubble up to a more generic part of the program to do a wider debug.

The way how we can do this sort of "bubble up" exception catching, I do not know, but is my understanding correct? Can someone perhaps give a clearer explanation of C#'s "bubble up" exception behaviour?

then about throw:

I see there is throw and throw new.

From what I've seen, throw is used when you check for a particular error/condition in your code and you want to warn the outside block that particular condition occurred.

Is this correct? I don't really understood why I have to use throw or throw new and the differences between them.

closed as unclear what you're asking by MetaFight, enderland, Robert Harvey, Ixrec, user40980 Jan 8 '16 at 2:27

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • @RobbieDee Google is my friend, but wordy tutorials arent. I mostly use google for every doubt I have if I can't solve myself. I resort to the stackexchange when I bounce my head multiple times on explanations which don't help me because are badly explained or I don't get. I've googled and searched here a lot, but I couldn't find an explanation which really clarified me my doubts... – Liquid Core Jan 6 '16 at 16:28
  • 4
    What is unclear about the answer Robbie pointed you to, Liquid Core? SLask very clearly explains the difference between throw, throw exceptions and throw new Exception. Is that not what you wanted to know? – Andy Jan 6 '16 at 16:32
  • 3
    I reread the question. Including the update. The question linked to by Robbie Dee adequately answers the question as asked. If you feel it's inadequate, you will need to rephrase your specific question more clearly. – Joel Etherton Jan 6 '16 at 19:26
  • 6
    "In my daily job, I've used just try/catch to mostly control the flow." - Exactly what exceptions are NOT supposed to be used for. – Kevin Krumwiede Jan 6 '16 at 19:29
  • 1
6

When an exception is thrown, the framework will walk up the call stack until it finds an exception handler that handles the type of exception you threw. If there is no such exception handler, the program ends. So you can provide an exception handler anywhere in the call stack where it is convenient to put one.

enter image description here

In general, exceptions should be used for situations that your code cannot do anything about or recover from without user intervention. If your user provides a file name that does not exist, for example, your code cannot read the mind of the user to get the correct file name, so it throws an exception.

The difference between throw and throw new is that throw new throws the original exception, while throw is used when you want to catch the original exception, do something with it (log it perhaps), and then rethrow it so that it continues to bubble up the call stack while preserving the original stack trace.

Generally speaking, you won't usually need custom exception classes unless you want to do something exotic like serialize your exceptions or capture specific data during the throw new (for diagnostic purposes).

An Interface has but one purpose: to decouple an implementation. There are many reasons why you might want to do this, but they all amount to essentially the same thing: having the ability to provide multiple implementations that conform to the same interface specification.

5

So, first things first, programming language features and computing concepts in general are just tools. Your question indicates that you don't really seem to think this way. You think of needing to do something first, and find some pattern of code accomplishes that. I cannot stress how strongly how much this will limit your abilities as a programmer in general, and your career specifically.

I am going to explain exceptions from the tool side. Here is what they are. Here is how they work. Understanding the implications of that, and creating new, interesting, and wonderful stuff from them becomes your job.

now I need something in the style "Explain it like I'm 5" for exception throwing.

As your program executes, it progresses from one function to another. This eventually builds a path from the entrypoint to the current execution point in the program. The code itself is largely unaware of what function called it.

This leads to problem when things go wrong. When things go wrong in a function, it doesn't know who called it, so it doesn't really know what to do. Enter exceptions. Exceptions are there for the function to say "oh shit, something went wrong! I don't know what to do! I hope the stuff that called me knows what to do...".

What happens is that a function throws an exception, and then the computer goes back along the path of execution that the program built up until it finds some function that actually knows what to do. If no function is found, the OS generally just kills your program because something went horribly wrong.

That's exceptions in a nutshell.

In C#, the throw statement is how you tell the function to fire off that exception and go back along the execution path. The common usage of the statement takes an Exception object as an argument. Since you almost always have a new Exception, that's why you use new - just like any other object.

catch is how you tell the computer "I know how to deal with this sort of error if it happens here".

There are some other nuances around try/catch in C#, and other languages have some variation in how they work, but that general concept is fairly ubiquitous.

  • Didn't understood a word after the word "nutshell". That doesn't explain how I'm gonna use them, wheter they are tool or not. You could have the most wonderful wrench and yet you can't plant a screw, unless you use it awfully wrong like a hammer to plant the screw. I needed to see how these worked, not what they might do. – Liquid Core Jan 6 '16 at 18:23
  • 5
    @LiquidCore - I literally cannot spell out all of the different scenarios where you might use exceptions or how they may work in those scenarios. I can explain concepts, but you're going to have to learn how to adapt them to your situation to be a vaguely competent programmer. – Telastyn Jan 6 '16 at 19:27
3

The purpose of an exception is: To halt execution because an error occurred and you cannot do the next operation.

The purpose of catching an exception is: To retry if possible or do whatever other corrective measures you may in the event of the given error scenario your application is in.

in C# an Exception is an object which:

  • Inherits from the System.Exception class
  • May be instantiated just like any exception by writing new WhateverException();
  • May be thrown with the syntax: throw instanceOfAnException;
  • May be caught with a catch statement that specifies the type of exception or any base-class that exception inherits from.
    • Since all exceptions inherit from System.Exception you can catch any exception with the clause catch(Exception e) { /* ... */ }
  • May be thrown again from inside a catch block with the statement throw; without needing to specify the instance being thrown (it automatically re-throws the exception instance that had been caught)

When an exception is thrown

  • The current statement block terminates immediately where it is, and checks if there's a catch block in it's parent blocks scope.
  • If no catch is found right away, it "bubbles up" scopes through statement blocks and functions terminating each one exactly where it is, and looking for a catch block which would match it's type or one it derives from.
  • If no catch is found all the way up to the main(), then the main() will terminate ending the entire application process.
  • If a matching catch is found, the application begins executing at the point of that catch block, and continues on from there.

A catch block

  • May specify a type of Exception it catches.
    • If it does, that type and any types that inherit from it will be caught in this block if they aren't caught in a lower-scope.
    • If it does not, any and all Exceptions that are thrown will be caught in this block.
  • May specify a variable name so that the Exception is addressable as a variable when caught as catch(Exception variableName) { /* do stuff with variableName here */ }
    • Does not have to specify a variable name and may simply catch(SomeExceptionType) { /* the exception is not available here */ }
  • May throw; without specifying which exception to throw and it will simply throw the exact Exception that was caught.
2

"Explain it like I'm 5" for exception throwing.

Exception-handling is probably best thought of as modeling a transaction mindset. Forget about the low-level control flow aspect. There is a control flow aspect to it, but it's like we don't want to use functions as an elaborate goto for branching. There's a higher-level concept and mental model associated with that which is better to focus on.

The idea is that things either succeed as a whole or fail as a whole.

try
{
   // Transaction site. Try things out.
}
catch (Exception e)
{
    // Recovery site. We might report a message to the user
    // and also reverse any side effects (things we tried that
    // didn't succeed as a whole, like deleting a file that we
    // only managed to write half the contents of).
    ...
}
finally
{
    // Post-transaction site. Do things here that you need to
    // do after the transaction whether or not it succeeded or
    // failed, like closing a file.
}

External Error vs. Programmer Error vs. Non-Error

Now, unless you're working in Python which is totally bizarre to me with how they go about exceptions ("leap before you look"), normally the process of throwing exceptions is best utilized for external errors.

By external errors, I mean things you cannot help and are outside of your control. For example, trying to open a file might fail due to some environmental condition in the operating system (maybe the hard disk is full). Your program can't really help that so much. Maybe the user interrupted and aborted the middle of a loading process -- you can't anticipate that. Maybe allocating memory failed. All of these errors are the result of errors occurring externally, outside of your code.

This is the most natural place from which to throw an exception.

Programmer errors may or may not be tackled through exceptions. In C++, it's often discouraged in favor of an assertion mindset to catch programmer mistakes like accessing an array out of bounds, but I think C# might encourage throwing here. So a programmer mistake may or may not involve throwing exceptions depending on the language.

Outside of Python which has this "leap before you look" philosophy, generally you shouldn't throw for non-errors. By a non-error, I mean trying to find a key in a map and failing to find it. That's not an error so much as just a very valid post-condition for a find function (it didn't find it, very ordinary thing).

Throwing

When you throw an exception, you are reporting an error. The idea is completely decoupled from catching. Don't think about where an exception will be caught or how it will caught when you throw. That would tend to couple your function to the transaction and the call stack, and reduce generality. Potentially you might reuse this function in various transactions, but it can fail the same way. Throwing is kind of a shortcut to avoid manually having to return error; return error; return error; all the way down to the catch site, but try not to look at it that way so much.

Throwing is about reporting an error. It's broadcasting it to whomever is interested.

Throwing is Expensive

If this conceptual model of avoiding the throw for non-errors (like failing to find an item) is confusing, it's worth noting that throwing an exception can be very expensive (depending on language/compiler). Some implementations have to kind of suspend threads and stop the entire world.

You might equate it to the cost of a disk access. I don't know how expensive it is exactly and it seems to vary wildly from one language/compiler to another (in Python I imagine it has to be cheap), all you might need to know is that it is expensive. And that's why we don't want to necessarily use it to just report back non-errors to the caller that a key isn't found in a dictionary, e.g., in languages where this kind of thing isn't idiomatic. It's not a general branching mechanism, it's for genuinely exceptional paths.

Rethrow vs. Throw New

With transactions, you also have sub-transactions. For example, let's say that in the process of loading an HTML file (trying to do this in a transaction), and you come across an image tag specifying an image file.

Now you might start a sub-transaction to load an image, and perhaps we fail to load the image and encounter an exception. In that case, you might catch it inside your nested image transaction.

try
{
   // Load image file and add the image to the webpage.
}
catch (Exception ex)
{
   // We ran into an error loading the image.
}
finally
{
   // Close image file if it's open
}

However, this is a sub-transaction. We've encountered an error loading the image but it's also an outer error in loading the HTML file. As a result, we don't want to simply catch the exception and return from the function. We need to throw again. Here you can do one of two things:

catch (Exception ex)
{
    // Translate the exception into a more meaningful one
    // for the user.
    throw new ImageException(...); // can embed a message containing
                                   // details about what happened.
}

Swallowing up an exception and translating it this way might sometimes be useful if you want to try to report the error in a less "raw" kind of way to the caller of your image loading function.

... or you can do this:

catch (Exception ex)
{
    // Simply throw the same exception to the outer transaction.
    throw;
}

... and simply pass the original exception back to the caller.

Which one you choose here is more of a design decision, whether you want to simply forward the exact same exception to the outer transaction that invoked the image loader or translate it into some kind of specific image loading exception.

It's up to you and will be based on how rich you want the error reporting to be. I personally prefer dealing with fewer exception types and catching more generally since most of the time I don't find much use for an exception type beyond just grabbing the message attached, but that may be my C++ side speaking where programmer errors aren't reported through exceptions (they don't point out mistakes, only those external input errors outside of our control that can occur even when our code is perfectly correct).

In cases where exceptions are used in a debugging context more than just a "report what happened to the user" kind of mindset, you might find it very helpful to know exactly what kind of exception was thrown, and catch more specifically when possible as well as throwing a wider range of exception types.

  • 1
    Perhaps you could go a little further into catching different types of exceptions with one try catch block. I find that just using the general Exception class leads to masking of harder to find errors. – Ampt Jan 6 '16 at 20:08
  • @Ampt Here I might be straying from C#-idiomatic territory and applying too much of my C++ mindset, since in C++ programmer errors typically aren't reported through exceptions (they don't occur as a result of a mistake), only strictly those external ones which aren't in the "debug" territory. Probably in languages that throw on programmer errors, catching the precise exception type becomes a crucial aspect of debugging to know exactly what happened. I'll see if I can try to update the answer (I'm coming at this from a somewhat language-agnostic standpoint). – user204677 Jan 6 '16 at 20:11
  • 1
    @Ampt I think in the cases where you can choose to rethrow or throw new inside a catch block, probably rethrow is going to be the easiest for debugging. I'm venturing outside of my area with too little experience in C# and Java, but I would imagine that choosing to throw new ImageException(...) might be useful if you are a library designer building an image library, and you want to avoid exposing the raw internal details of the various types of exceptions that image loading can encounter. It would make debugging harder from a broad perspective, ... – user204677 Jan 6 '16 at 20:32
  • 1
    I would agree with that - if your goal is to provide an API-like experience. For general programming purposes, I would stick to rethrow, as the stack trace will clearly show where and why the exception was thrown. – Ampt Jan 6 '16 at 20:35
  • 2
    On "Throwing is expensive" - the expensive bit is the generation of the stack trace (at least in the JVM). For this reason there is a NoStackTrace in Scala which suppresses the stack trace generation. See also How slow are Java exceptions? – user40980 Jan 6 '16 at 20:38
0

Don't use exceptions to control program flow. For GimmeTheNumber() return a

NumberResult

object or similar.

public class NumberResult
{
    public int Number;
    public bool ResultValid;
}

private NumberResult GimmeTheNumber()
{
    int x = CalculateStuff();
    if (x == 0 || x == 15)
    {
       return new NumberResult(x, false);
    }

    return new NumberResult(x, true);
}

Then your code is:

var result = GimmeTheResult()

if(result.ResultValid)
{
   //Keep doing calculations unless the function result is 0 or 15

}

Now the code is cleaner. If you want, you could just put one exception handler at the function entry point to handle any unexpected errors while doing your calculations.

  • 4
    I really dislike objects like NumberResult as they are products standing in for unions. Better to use a proper union type or even just an int?. – walpen Jan 6 '16 at 17:33
  • I agree it is not optimal. Ideally I think it could return an int and then you could have a validator (class) to validate the result afterwards returning a true/false answer thus eliminating the intermediate class. But, I was mainly trying to show the avoidance of using exceptions to control flow. – Jon Raynor Jan 6 '16 at 18:03
  • 1
    Agree on the not using exception for controlling program flow, however, if 0 or 15 are conditions that should not happen under normal operating conditions, it would be valid to throw an Exception there. – Aaron Hawkins Jan 6 '16 at 18:11
  • Unless the application may continue even when the result is 0 or 15, I don't like this approach. Looks pretty much the same as having null checks all over your code, except you now have ResultValid check instead. – Andy Jan 6 '16 at 18:18
  • 1
    @LiquidCore as long as you persist in using them incorrectly (as control flow), explaining them will be counterproductive. Jon's example isn't circumventing exceptions - it is using the proper control flow for control flow and exceptions for exceptions. Once you acknowledge that the way you are using them currently is incorrect and other control flow structures may be used, then it would be possible to go forward and meaningfully explain how exceptions should be used. – user40980 Jan 6 '16 at 21:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.