views:

85

answers:

6

I've got some UI code that looks like this:

try
{
    SomeClass classInstance = new SomeClass(someId);
}
catch (Exception exception)
{
    // Content wasn't created, show a message, stop processing
    return;
}

It seems the try catch was added because the constructor for SomeClass would bomb out if the someId it receives isn't valid, and data couldn't be found in a DB.

Running this code through FXCop recently, it warns against using the general Exception, but all SomeClass does is throw a new Exception with a message to say it failed to initialize.

I guess the problem is that the class constructor should have it's own custom exception, which I could then handle in my UI, but I wonder what else I could do to the code above to handle the exception, that meets FXCop requirements?

+4  A: 

FxCop's rule exists because the catch (Exception) block above catches all possible exceptions, including low-level exceptions like StackOverflowException that you probably can't catch in a useful way.

The right approach is definitely to throw a more specific type: either one you've invented, or an existing .NET framework exception type that closely matches your situation. (When in doubt, I normally go for InvalidOperationException.)

Alternatively, you could check the exact exception type when catching it. This won't prevent the FxCop warning, but it should address the underlying problem:

catch (Exception exception)
{
    if (exception.GetType() == typeof(Exception))
    {
        // Content wasn't created, show a message, stop processing
        return;
    }
    else
    {
        // Some other exception type that wasn't thrown from our code -
        //    delegate to a higher-level exception handler
        throw;
    }
}
Tim Robinson
Thanks, this confirms what I thought, that if the author of SomeClass hasn't done his job properly, and you don't have access, you've pretty much got to exclude the warning from the FXCop report, because there's nothing you can do to please it.Thankfully in my case, I've inherited the work of the author, so I have full access to the source code and can do what I please - which in my case, will be to modify it to an ArgumentException.
Andrew Johns
A: 

Using InvalidOperationException in place of throwing Exception sounds like it might be sensible.

Jamiec
Looking at the exception description, this does sound like a reasonable fit, but I think I'd prefer to use that in operations other than in constructing the instance.I think in my case, ArgumentException is the better fit, as others have suggested, because the argument id is invalid.
Andrew Johns
+3  A: 

You don't need a custom exception; just use one of the dozens that already exist in the framework for given circumstances. If someId is bad, throw an ArgumentException -- that's what it's made for. If something's null that shouldn't be, a NullReferenceException will occur; just let it be thrown. Etc. Throwing a plain Exception is a bit like saying "something went wrong -- read the message for details" rather than "this went wrong".

FxCop is complaining about catch (Exception) because it's too commonly abused to swallow up all exceptions rather than letting them propagate and be handled by code that knows how to do so. You should be able to say what types of exceptions are being thrown and catch those, while letting those you don't recognize make their way up the call stack.

cHao
You should **never** throw `NullReferenceException`. Most of the time you could use `ArgumentNullException`. The rest of your post makes perfect sense though.
Igor Zevaka
You should never *have to* throw a `NullReferenceException` -- the framework will do that for you. It's easier to just let it propagate. But `ArgumentNullException` only makes sense for *arguments* (hence the name), and if what you're being passed isn't null, it'd confuse people if you threw it.
cHao
That's why I said most of the time. I found that 90% of the time where I want exception to happen when something is null - it's when that something is a parameter. For the rest of the cases, you are right, just let it happen.
Igor Zevaka
The problem is that you have to be very careful: there are a lot of standard exceptions that the framework will catch, and then you never know where it's gone. There's a published list somewhere on MSDN (no time to find it, sorry) that has about half a dozen exception types that the CLR explicitly does *not* catch - I'm pretty sure `NullReferenceException` is NOT on that list. So I'd agree wholeheartedly with Igor's original comment.
Dan Puzey
The framework eats exceptions? That seems a pretty odd thing to do, considering how evil it's considered to be.
cHao
+2  A: 

You should fix the class constructor. Throwing Exception is never a good idea.

However, to work around the issue you have temporarily (as this is a horrible, unreliable hack), you could check the message of the exception against the one you're expecting:

catch (Exception exception)
{
    if (exception.Message == "whatever your class sets the message to")
        // Content wasn't created, show a message, stop processing
        return;
    else
        // Any other exception should bubble
        throw;
}
Dan Puzey
would that actually prevent FXCop from complaining about catching Exception though?
Andrew Johns
No, it won't stop FxCop complaining, but it addresses the *reason* that FxCop complains - that catching `Exception` is far too wide a net.
Dan Puzey
A: 

If FXCop doesn't like handling the general Exception (and I tend to agree) then maybe you have access to SomeClass's source code. Modify the constructor to throw an exception that is more specific, e.g. ArgumentOutOfRangeException or some custom exception.

In that case your code would then look as follows:

try
{
   SomeClass classInstance = new SomeClass(someId);
}
catch(ArgumentOutOfRangeException exception)
{
   // Content wasn't created, show a message, stop processing
   return;
}
John
My question was posed in a "what if I don't have access to SomeClass's source code?" style. In this case, I do, and have already fixed it with a more relevant exception, but while doing so, wondered what would happen if I came across a class that I couldn't modify.
Andrew Johns
Throw it out and find another that isn't so badly written. Exceptions should have meaning; "Something went wrong" is meaningless, and would cause you hell if you were to get really involved in using the class and suddenly got a "something happened" error from somewhere in the bowels of your app. Such isn't the case when you're first starting, so switch before you write too much code that uses the class.
cHao
A: 

As many others have said, the constructor should not be throwing a naked Exception. Seeing that the constructor retrieves data from DB and throws based on the result, the best solution is to create your own exception class.

Creating exceptions is super-easy in Visual studio. Just type in Exception and press TAB. It will then create the exception class with required constructors(all four of them). Do not be afraid to create classes that don't do very much, that's what they are designed for.

This is how I would write this class:

public class SomeClass {
    public SomeClass(int someId) {
        if (someId < 0) //validation on the ID, can it be negative?
            throw new ArgumentException("someId", "ID cannot be negative");

        //Perform DB operation
        if (/*DB error - not found*/)
            throw new DataNotFoundException("Cannot find record with ID " + someId);
    }
}

[Serializable]
public class DataNotFoundException : Exception {
    public DataNotFoundException() { }
    public DataNotFoundException(string message) : base(message) { }
    public DataNotFoundException(string message, Exception inner) : base(message, inner) { }
    protected DataNotFoundException(
      System.Runtime.Serialization.SerializationInfo info,
      System.Runtime.Serialization.StreamingContext context)
        : base(info, context) { }
}
Igor Zevaka
Thanks, this is a useful shortcut - I've not created a custom exception before, but I'll remember this for when the time comes. For now, I'm using the standard ArgumentException, which suits my needs here.
Andrew Johns