views:

78

answers:

4

I am beginning to think I am doing something wrong. I mean they did place System.String.IsNullOrWhitespace finally but no ArgumentEmptyException class.

        public FilterItem(string name, IEnumerable<string> extensions)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException("name");
            }
            if (extensions == null)
            {
                throw new ArgumentNullException("extensions");
            }
            if (extensions.Count() == 0)
            {
                throw new ArgumentOutOfRangeException("extensions");
            }

            this.Name = name;
            this.Extensions = extensions;
        }

throwing an ArgumentOutOfRangeException feels unnatural. Also an instance of ArgumentException is too general in my opinion.

It's easy to me to create a new exception class call it this way and have it over with. What bugs me is that it's not in the BCL/FCL and I am beginning to think there's a good reason not to have it.

Should there be one?

+7  A: 

Personally, I would just raise an ArgumentException and put some information in the message. The message is just there to help you diagnose the problems during development--there's not really any situation where you would want to trap that error in a finished system, so there's no reason to make it uniquely catch-able.

JSBangs
Good point, haven't thought about the useless uniqueness..
Andrei Rinea
+4  A: 

You should just use ArgumentOutOfRange for this. After all, its documentation says:

The exception that is thrown when the value of an argument is outside the allowable range of values as defined by the invoked method.

And there's nothing to imply that it is only to be used for numeric types. In your case, the "allowable range" is all strings except for the empty one.

By the way, since you're using .NET 4 already, why not just use Code Contracts? It doesn't really make much sense to define unique exception types for those exceptions which are, effectively, contract violations - they are never supposed to be caught...

Pavel Minaev
I could use the Code Contracts, but still my dilemma regarding the exception type would stand.
Andrei Rinea
You don't need to specify exception type for code contracts. You can do so, but it's really meant more for legacy code which always threw a particular exception type. The default behavior for `Contract.Requires` is to throw `ContractException`, which is, in fact, `internal` - so you can't even catch it (as it should be).
Pavel Minaev
@Pavel: When using code contracts while developing a reusable framework, specifying exceptions is needed, because you can't be sure all your users will use the static checker of code contracts (it's only available in the VS2010 Ultimate edition). Therefore it is still important to throw a the correct exception type. Of course for LOB applications, your argument still holds.
Steven
You don't need to use static checker. You just use the dynamic rewriter, which _will_ throw on contract violations. Why do you care about the particular type of the exception that will be thrown in that case? It will include the correct message text clearly identifying the problem, and the type is only relevant if client tries to catch the exception - which he should never, ever do with a contract violation, anyway.
Pavel Minaev
+2  A: 

Defining new exception types should in most cases only be done for exceptions that should be caught. In this case you don't want anybody to catch an exception (you should rarely catch ArgumentExceptions), so it wouldn't make much sense creating a new exception.

In that case you should pick the exception type that describes your exception best. I don't believe this is ArgumentOutOfRangeException, because it is hard to see a string a something 'rangeable'. Your best pick is ArgumentException and the type does describe what is wrong (an argument).

Sometimes it can be useful to define a new exception type, even if nobody is intended to catch it. In the CuttingEdge.Conditions library I defined a PostconditionException, just because throwing an ArgumentException felt wrong when validating postconditions.

For framework developers defining new exceptions (that should not get caught) can sometimes be a good thing, because it allows better communicating what went wrong. When working on line of business applications however, I hardly ever define exceptions that I don't want anybody to catch. I simply use the existing exceptions.

BTW. When using CuttingEdge.Conditions, your code example can be rewritten as follows:

public FilterItem(string name, IEnumerable<string> extensions)
{
    Condition.Requires(name, "name").IsNotNullOrWhiteSpace();
    Condition.Requires(extensions, "extensions").IsNotEmpty();

    this.Name = name;
    this.Extensions = extensions;
}

Last note, I would rewrite the last line of code to the following:

    this.Extensions = extensions.ToArray();

Because when you require the collection to be non-empty, you don't want anybody to clear it after to checked this.

Steven
I think the 'range' in `OutOfRange` is meant in the mathematical sense of a set of values, rather than necessarily the interval between a start and an end. Anyway, since strings can be ordered, they *are* 'rangeable' in that sense!
AakashM
@AakashM: I agree with you that strings can be put in ranges (for instance 'aaa' -> 'zzz'), but I consider this rare. I must say I have had a lot of thought over the `OutOfRange` (OOR) when developing Conditions. For instance, it throws an OOR when a call to `IsInRange(x,y)` fails (which is obvious). However, should it throw an OOR when a call to `IsNotInRange(x,y)` fails? And what about `IsLessThan` and `IsNotGreaterThan'? I desided to throw an `ArgumentException` in the case of `IsNotInRange`, but in the end, is doesn't really matter, as long as you throw an ArgEx with a good message.
Steven
+2  A: 

There are precedents for throwing ArgumentNullException in the String.IsNullOrEmpty case - for example System.Windows.Forms.Clipboard.SetText. See also this related question.

So by extension I think it's reasonable to do the same thing for String.IsNullOrWhitespace. ArgumentOutOfRangeException is also a reasonable choice, but personally I would only use it when the argument expects a more restricted range of values than just non-empty.

Advantages of reusing ArgumentNullException (or ArgumentOutOfRangeException) rather than ArgumentException or a custom Exception type:

  • ArgumentNullException (and ArgumentOutOfRangeException) has a constructor that takes string paramName - so no need to generate a message like you would have to with ArgumentException.

  • No need for the extra work of creating a custom Exception type.

The only disadvantage I see is if the caller needs to handle this exception differently from the null case, which seems highly unlikely to me, and if taken to its logical conclusion would lead to what is patently overkill:

if (param == null) throw new ArgumentNullException("param");
if (param.Length == 0) throw new ArgumentEmptyException("param");
if (param.Trim().Length == 0) throw new ArgumentWhitespaceException("param");

I would generally go for ArgumentNullException, which is how I would interpret the following MSDN guideline:

Do create and throw custom exceptions if you have an error condition that can be programmatically handled in a different way than any other existing exceptions. Otherwise, throw one of the existing exceptions.

Joe
`ArgumentException` also has a constructor which takes `string paramName` - http://msdn.microsoft.com/en-us/library/sxykka64.aspx
Pavel Minaev
"ArgumentException also has a constructor which takes string paramName" - that constructor also takes a message parameter, so the caller needs to supply a message (and potentially consider whether the message must be localized...).
Joe