Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed last year.
Improve this questionI'm trying to figure out what the correct form of exceptions to throw would be for a library I am writing. One example of what I need to handle is logging a user in to a station. They do this by scanning a badge. Possible things that could go wrong include:
- Their badge is deactivated
- They don't have permission to work at this station
- The badge scanned does not exist in the system
- They are already logged in to another station elsewhere
- The database is down
- Internal DB error (happens sometimes if the badge didn't get set up correctly)
An application using this library will have to handle these exceptions one way or another. It's possible they may decide to just say "Error" or they may want to give the user more useful information. What's the best practice in this situation? Create a custom exception for each possibility? Use existing exceptions? Use Exception and pass in the reason (throw new Exception("Badge is deactivated.");
)? I'm thinking it's some sort of mix of the first two, using existing exceptions where applicable, and creating new ones where needed (and grou开发者_开发技巧ping exceptions where it makes sense).
I essentially agree with your current thinking.
- Use existing core exceptions where appropriate: ArgumentException, InvalidOperationException, etc. Don't try to repurpose exceptions that are specific to some other module. Use those exceptions that have a clear, generic purpose, and don't use them for business rules. For example,
InvalidOperationException
should indicate a bad operation w/ regards to your API, not a violation of a business rule. - For exceptions specific to your library, create a base exception class,
BadgeAuthException
, and always throw that. The specific scenarios should each get their own subclass (BadgeDeactivatedException
,NoPermissionsAtWorkstationException
, etc.) This way apps can handle the individual sub-cases separately if they want to, but they can also just catch the genericBadgeAuthException
if they don't want to grovel in specifics. - Whatever you do, ensure that the
Message
field always contains useful information beyond just the exception name.
Use Exception and pass in the reason (throw new Exception("Badge is deactivated.");)
This certainly is a bad practice, because it violates the purpose of exceptions - not just to signal an abnormal situation, but provide an ability to distinguish exceptions on a type level, so the user of a module can make a decision depending on a type of exception.
Generally, it is good to reuse standard exceptions as far as they can fully describe abnormal situations your code actually faces. It is quite hard to give an advise in a current situation, because exceptions can often depend on semantics ( argument exception or invalid operation exception (maybe suitable for 'Their badge is deactivated' case, for example ).
You have two species of exceptions.
Those that are specific to your application, where it's good to avoid any existing exceptions.
Your application-specific exceptions should simplify the use cases for the folks who use your libraries. 3 of your application specific exceptions are things users can do. The fourth (badge does not exist) is clearly not procedural, but far more serious.
It looks like you have two application-specific errors: user-oriented things and administrative mistakes.
The others are part of some other piece of technology; i.e., database errors. You can -- generally -- ignore these. If the DB isn't available, the API will throw errors and you can let those bubble up through your library.
You can also "wrap" these as an application-specific exception which contains a lower-level exception. This is sometimes helpful if there is a lot of lower-level technology. In your case, it's just a database. Ignore it and let the DB errors bubble through.
You can nearly never go wrong by having fine-grained exception classes that extend a common base class. That way, callers who need to specifically catch some and let others through can, and callers who want to treat them all together can do so.
I think you need to have one base exception and one subtype exception for each of the situalitions you describe (actually you can make db down and internal db error to have the same base exception). The key point is that it is good practice to have your own exceptions for your library.
精彩评论