9

I've used try and catch statements as an easy way to keep my code running without things crashing (I would wrap everything in a big try). Recently, I've wanted to start using try and catch statements more correctly. Here as an example I have questions about:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        try{
            if(numberOfShirikins == 0){
                throw new System.ArgumentException("Invalid number of shirikins");
            }

            //Throw shirikin
        }
        catch(ArgumentException e){
            MessageBox.Show(e.Message);
        }
    }
}

In the above Ninja class, the entire contents of my ThrowShirikin method is contained in a try loop. Since there is only one opportunity for an input error (in this case, when numberOfShirikins == 0), shouldn't only the lines of code that check for this be contained in the try loop? See below:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        bool errorsExist = false;
        try{
            if(numberOfShirikins == 0){
                errorsExist = true;
                throw new System.ArgumentException("Invalid number of shirikins");
            }
        }
        catch(ArgumentException e){
            MessageBox.Show(e.Message);
        }

        if(!errorsExist){
            //Throw shirikin
        }
    }
}

^But what I have here seems a bit clunky. Any suggestions and input on how I'm understanding the use of try catch statements? Thanks!

Edit:

Or I could do something like this so the //Throw shirikin code never executes if there is an invalid value for numberOfShirikins?:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        try{
            if(numberOfShirikins == 0){
                throw new System.ArgumentException("Invalid number of shirikins");
                return;
            }
        }
        catch(ArgumentException e){
            MessageBox.Show(e.Message);
        }

        //Throw shirikin
    }
}
sooprise
  • 22,657
  • 67
  • 188
  • 276
  • Your errorsExist = true; will never be run, since the throw command will send the execution in the catch. So errorsExist will always be false. – Gabriel Mongeon Feb 14 '11 at 20:40

9 Answers9

5

When you throw exceptions like ArgumentException you shouldn't catch them in your method. Give this work to client for your method.

whyleee
  • 4,019
  • 1
  • 31
  • 33
  • +1 throwing exception help the call stack to catch the error at a higher level. – Gabriel Mongeon Feb 14 '11 at 20:42
  • I see, so my methods throw the exceptions, and the callers of those methods will decide what to do with them? So like I'll call ThrowShirikin(3); and put that in a try? – sooprise Feb 14 '11 at 20:54
  • @sooprise, right. *If the caller is prepared to deal with it*, the caller can catch it. But *only* catch what you are prepared to deal with. `catch (ArgumentException ex)` is fine, `catch (Exception ex)` or just `catch` is simply not preferrable. – Anthony Pegram Feb 14 '11 at 20:57
  • 1
    @soo: if you expect your callers to try to throw too many shuriken, then I think that, in addition to throwing `ArgumentException`, you should have a `bool TryThrowShuriken(int n)` method that will return false if `n` is too large. Keep the `ArgumentException` in `ThrowShuriken` in case of cheaters, though. – John Saunders Feb 15 '11 at 20:54
4

It looks like the try/catch only exists to catch an exception you are choosing to create and throw, and then you catch it just to pop a message box to the user. Then you choose to either continue or not based off the error condition.

Eliminate the try and the catch. Eliminate the message box. Do throw an exception for an invalid argument. Let the caller of the function determine if they want to catch and how they want to handle. Your Ninja class should not be making these decisions beyond identifying what is valid and what is not.

if (numberOfShirikins == 0)
    throw new ArgumentException("...");

// rest of your shirikin throwing code
Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
  • I see, so when an exception is thrown, it already gives the user the choice to quit or continue, so my messagebox was not necessary – sooprise Feb 14 '11 at 20:46
  • @sooprise, that's for the calling code to decide. If the calling code further prompts the user and then calls back into Ninja, that's fine. Ninja is simply telling the calling code (via the exception) that inputs were invalid. The calling side can then determine what action to take, if any. In other words, have a try and a catch. *Just not here.* – Anthony Pegram Feb 14 '11 at 20:48
  • So it's my responsibility to write these throw exceptions, but not my responsibility to catch these exceptions myself? Anyone who uses Ninja.ThrowShirikin should have some try catching him/herself? – sooprise Feb 14 '11 at 21:06
  • @sooprise, almost. Here's what you really want to do. `Ninja` should definitely reject invalid inputs. Callers, if so inclined, can certainly catch these exceptions and deal with them. *But* the callers should try to *prevent* these invalid inputs from even reaching `Ninja` in the first place. Follow? So if you're coding a client to `Ninja`, then *you're* going to want to anticipate invalid inputs. "Ninja doesn't like 0? Then I won't even bother Ninja *with* 0, I'll ask for something else." – Anthony Pegram Feb 14 '11 at 21:13
  • Sorry, I'm having a hard time understanding this. By telling me I should anticipate invalid inputs, isn't throwing exceptions enough to indicate that I am anticipating invalid inputs as I throw exceptions when there are invalid inputs? (in other words, I'm doing something when there is an invalid input, which shows that I am anticipating them properly?) – sooprise Feb 14 '11 at 21:16
  • What I mean is this. Let's forget `Ninja`. Let's say you're calling somebody else's function. You know for a fact that this function throws an exception if you give it invalid input. You know what this invalid input is. You have a means of validating against that invalid input. Are you going to call the function with input that you have not validated? Back to `Ninja`. It says what input is invalid, and it will let you know it doesn't like it. You're coding against that class, you know what input it doesn't like. Are you going to bother calling the class with that input? – Anthony Pegram Feb 14 '11 at 21:21
  • As a provider of a library, you certainly want to throw in exceptional situations. As a consumer of a library, you want to anticipate the exceptions before they become exceptional. Making sense? – Anthony Pegram Feb 14 '11 at 21:22
  • Anthony, I think I do understand now. Before it seemed like merely throwing an exception was not enough as you don't have any code to handle it, but from the perspective of the method, it only has to run properly, and if it's not fed the correct input, it shouldn't be expected to run properly (so it's ok that it doesn't handle the exception itself). I hope that's right.... – sooprise Feb 14 '11 at 21:28
  • Right. `Ninja` is doing it's job. It's telling you what will not work, and it's doing so in an appropriate manner. A consumer of `Ninja` can handle the exception but can *also* say "well, Ninja is not going to like 0, so before I even give it 0, I'm going to ask the user for something else." – Anthony Pegram Feb 14 '11 at 21:35
2

You should never catch exceptions in this way. Only catch exceptions that you can actually handle.

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
            if(numberOfShirikins <= 0){
                throw new System.ArgumentException("Invalid number of shirikins");
            }
        //Throw shirikin
    }
}

I suggest that you start programming without ever using try/catch. Then fix any exceptions you see, don't hide them.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
1

There is no real reason to catch exceptions in the same method like that. Go without:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        if(numberOfShirikins == 0){
            MessageBox.Show(e.Message);
            return;
        }

        //...
    }
}
Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
  • Is using a return; and exiting out of the method preferable over using a bool like errorsExist and an if(!errorsExist){//Throw shirikin} to continue the code if there are no errors? – sooprise Feb 14 '11 at 21:04
1

A method should not be capturing exceptions that it throws itself - it's fine to generate them, but understand that they are expensive and that normal flow control is preferred (it's easier to reason about and such).

Further, in the above example, errorsExist will never be set to true - that should be put in your catch block rather than directly under where you're throwing an exception. That terminates the normal program flow.

Restated, method would look like this.

public void ThrowShirikin(int numberOfShirikins) {
    if(numberOfShirikins == 0){
        throw new System.ArgumentException("Invalid number of shirikins", "numberOfShirikins");
    }

    //Throw shirikin
}

It would be up to the class calling the method to decide what to do with the exception.

48klocs
  • 6,073
  • 3
  • 27
  • 34
1

Neither is a good idea. There is no sense in throwing an exception and catching it in the same method and to display a forms dialog from that class.

Only classes that should contain GUI code are the ones in your GUI.

In your case, I wouldn't use an exception either, I would use a boolean return value:

public bool ThrowShirikin(int numberOfShirikins){
            if(numberOfShirikins == 0){
                return false;
            }
            //throw
            return true;
    }

Exceptions should be used for exceptional cases, not the general, which this would be.

Also, be aware that the errorsExist = true; code in your second way will never be called as it is below a throw statement.

Femaref
  • 60,705
  • 7
  • 138
  • 176
  • You bring a good point about what is exceptional and what is not. I would argue that in your code, you still have an exceptional possibility, I cannot possibly throw -4 items, I don't think. – Anthony Pegram Feb 14 '11 at 20:46
  • throwing -4 shirikins is like catching 4 shirikins being thrown at you ;) – sooprise Feb 14 '11 at 20:56
1

When you throw an exception, you're basically saying: "This is an exceptional situation that I don't know how to handle (or another method higher up would have a better idea of how this should be handled)"

So what the method you just posted is saying then is: "I don't know what to do when I'm asked to throw 0 shurikens. Actually I lied, I'm going to show a message box"

Either skip the exception all together and show the message (not ideal, but better than throwing than what you have), or just throw the exception and catch it higher up in the UI code. You may want to create your own exception type if you want to handle this specific one specially.

Davy8
  • 30,868
  • 25
  • 115
  • 173
0

IMO - an Exception is really an exceptional condition in your code - i.e. conditions which cannot be anticipated and/or cases where the error can error in truly exceptional conditions.

If the code snippet you have shown above, have an numberOfShirikins == 0 is not an exceptional condition, it is a normal argument check

So, I would re-factor the code to:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        bool errorsExist = false;
        try{
            if(numberOfShirikins == 0){ showUserMessage("Invalid number of shirikins", MessageType.Information); }
        }
        catch(ArgumentException e){
            showUserMessage(e.Message, MessageType.Exception, e);
        }

        if(!errorsExist){
            //Throw shirikin
        }
    }
   private showUserMessage(string message, MessageType type, Exception ex = null){
        MessageBox.Show(message);
   }

}

Once this is done, you will find that there is really no value addition done to catching the exception, so we might be able to write a generic exception handler to show exception & let your code flow through normally.

Now, my code would look like:

public class Ninja{
        Ninja(){
        }

        public void ThrowShirikin(int numberOfShirikins){
                if(numberOfShirikins == 0){ showUserMessage("Invalid number of shirikins", MessageType.Information); return; }
                else{ /* do something here */ }
        }

On the other hand, if the numberOfShirikins == 0 is an exceptional condition, then I will write a custom Exception with the code as:

public class Ninja{
        Ninja(){
        }

        public void ThrowShirikin(int numberOfShirikins){
                if(numberOfShirikins == 0){ throw NumberOfShirikinsException(); }
                else{ /* do something here */ }
        }
Sunny
  • 6,286
  • 2
  • 25
  • 27
0

If you can avoid to throw exceptions, you should. They cost about 1000-4000 clock cycles.

Bengie
  • 1,035
  • 5
  • 10
  • Instead of throwing exceptions, how can I notify the caller of the function that there is a problem? – sooprise Feb 14 '11 at 21:57