tags:

views:

126

answers:

10

I want to gracefully handle a specific exception with a certain message. Unfortunately, it's just an ArgumentException, and not specific to what I'm looking for. In this case, the message is "An item with the same key has already been added". This IS an exceptional case, but I want to handle it so that I can either turn it into a specific exception or inform the user with non-techy terminology.

It seems like a bad idea to code it to look for that message, but what else can I do?

+2  A: 

You can create your own exception that derives from the Exception class.

For example:

public class KeyExistsException : Exception
{
    // ...
}

However, if it is an exception from a third party component, there is little else you can do than check for a message, other than the ContainsKey method. An ArgumentException doesn't have anything but a String to provide information about what it's about.

nasufara
A: 

I agree that this smells, but it looks like you don't have a choice...

Oded
+2  A: 

Never use the Message property for anything other than display to a human. What if they need to change the punctuation on the message, or, God forbid, change the spelling or the wording?

John Saunders
Or even worse, non-english localized messages. The stock exceptions will localize themselves to the machine's settings.
Joshua
+4  A: 

Are you the one adding the item? If so, write code to avoid it happening in the first place.

It should be very rare to handle an ArgumentException, and incredibly rare to handle it based on the message. Basically you should only be forced to do this when everything within your power has failed, e.g. it's a third-party library which is causing the exception. (In that case you should contact the third party to tell them to buck their ideas up, too.)

EDIT: Instead of calling ToDictionary, call ToLookup. You can then check for any results which have multiple entries.

Alternatively, write your own version of ToDictionary which handles this in the correct way for your situation - it's pretty simple.

Jon Skeet
I'm running a query, and then using the ToDictionary extension. It is true that I could check the results of the query, but that's extra code and processing time for something that is the exception to the rule, and should not normally happen.
Jason Young
not entirely. run an "AssertUnique" extension on your query result, then ToDictionary. you will have to authour said extension of course, but it would look like `IDictionary results = query.AssertUniqueKeys ().ToDictionary ()`
johnny g
I understand what you're saying, and that's probably the route I'll take, but you have to admit it doesn't feel right.Also, I have run into a different situation where it was a SqlException and I couldn't use this kind of "hack".
Jason Young
@Jason: It feels right to me. You're aware that you've got potentially bad data - so you're checking it as you go rather than letting something else fail. It's exactly the same as using int.TryParse with user data rather than catching the exception and then handling that.
Jon Skeet
+1  A: 

You could avoid the exception and check the dictionary with .ContainsKey method before you add new entries.

David R
I'm running a query, and then using the ToDictionary extension. It is true that I could check the results of the query, but that's extra code and processing time for something that is the exception to the rule, and should not normally happen.
Jason Young
A: 

I've done that before and lightning hasn't struck me down. Yet.

Fox Mulder
Who dared downvote this?
Joshua
A: 

You'll probably have to do it this way. But I would really suggest thinking about your design. This seems like a symptom of a questionable design.

try {
    // your code
} 
catch (ArgumentException ex) {
    if (ex.Message != "An item with the same key has already been added") throw;

    // handle your case
}
recursive
What happens if a different message string happens to be used? There's nothing about `ArgumentException` that dictates the value of the `Message` property. It's an implementation detail, subject to change at any time.
John Saunders
I agree. I wouldn't use this approach.
recursive
Yeah, this is exactly the dilemma I'm referring to. :-)
Jason Young
+2  A: 

If you're building a dictionary, using the Enumerable.ToDictionary method, then an ArgumentException always means a duplicate key. You don't need to check the message at all, just put the ToDictionary call in its own statement and catch ArgumentException.

This is mentioned in the Enumerable.ToDictionary documentation.

More generally, handling any exception based on the message itself is almost always a bad idea. Among other things, exception messages may be localized, and the text will be different depending on the operating environment.

Aaronaught
Hmm, initially that looks perfect, but I suppose this could change just as easily as the message.
Jason Young
There's no need to wait for Enumerable.ToDictionary to throw an exception - it's incredibly easy to write your own version which does what you want instead. Don't forget that if the data source is null somehow (or a mapping function) that could still throw ArgumentNullException - so you could end up hiding a nasty bug that way.
Jon Skeet
The ArgumentException is less likely to change than the message itself, because it's actually documented (and hasn't changed sense version 2 of the .NET Framework). Writing out a "safe" version with ContainsKey calls is one option, but won't perform as well if there are no duplicates in a majority of instances.
Aaronaught
A: 

If this exception is in third-party library, and there is definitely no way of making your own exception, then i would go ether with looking at the 'StackTrace' or 'TargetSite' properties of the exception, cause they both give you quite the same class names that can't be changed by switching locales or something like that.

Ereleno
+1  A: 

Another reason not to filter exceptions based on the message is that the message might be localized! This is especially true for exceptions thrown by .NET framework components.

Mike LaSpina