Possible to force a developer to handle specific exceptions?
Essentially, I'd like a special form of an Interface
for Exceptions that requires anyone who uses my object to wrap it with specific catch
implementations.
Example
I have an object that sends data to another host. I expect that the realistic implementations will require a way to handle the following exceptions:
- HostNotFoundException
- InvalidUsernameException
- AccountExpiredException
- DataAlreadyExistsException
Similar to how an Interface
or an Abstract
class is used to force the creation of methods and properties in derived classes, is there any way I can force a consumer to implement exception handling the way I expect?
On a similar note, I'd also like to force methods (created via Interface or Abstract) to be able to generate certain exceptions. Sure they may be NotImplemented, but I want to tell that developer (who doesn't read documentation) that they 开发者_开发问答should be considered.
Goal
The benefit of this exception checking is to enable more robust error handling. This would be accomplished by the consumer using the object, and the object creator.
Solution?
The only approach I can think of is T4 templates, but that isn't as complete of a solution as I would like. I'd love to see this implemented in the language itself.
You can't force a programmer to do anything except jump through hoops. For example, let's say you have some method called Frob
that does something, and can throw FrobinatorException
. You expect programmers to write:
try
{
var result = Frob(foo);
}
catch (FrobinatorException)
{
// handle exception here
}
But you find that they don't. So force them to by defining Frob
like this:
public FrobResult Frob(FrobyThing foo, Action FrobinatorExceptionHandler);
And then programmers have to write something like:
var result = Frob(
foo,
() => { /* handle FrobinatorException here */; });
Programmers will grumble about having to do that and they'll end up writing this:
var StupidExceptionHandler = new Action(() => {});
var result = Frob(foo, StupidExceptionHandler);
And now you're worse off than you were because the exceptions are being swallowed, which hides bugs. It's better if the programmer just ignores the exception handling altogether. At least that way you know when an error occurs.
There's simply no way to force good exception handling. At least, not in C# as it currently exists. You can make it more convenient to handle exceptions, but doing so often makes it easier to hide exceptions by swallowing them.
If I'm reading your question correctly, it sounds like you're kind of looking for checked exceptions. There's an interesting article from much earlier in the development of C# that discusses this, actually.
From a design perspective, I don't really see how you could "force" the consumer of an interface to handle your exceptions. After all, how would you know it's being handled? Does the method which calls your interface need to wrap that call in a try/catch
directly? Or would it be sufficient for the method which calls that method to do so? Or for a global exception handler for the application to do so? It should really be up to the consumer of the interface to determine how/when/where to handle exceptions.
The best approach you can take is to document the potential exceptions in the intellisense comments on the interface. But this brings up an interesting problem which you also mention (if I'm reading you correctly). The problem here is that the documentation is on the interface, not on the implementation. What if one or more implementations throw different exceptions than those which are expected by the interface?
In general, I think the balance to be reached here is still to document potential exceptions on the interface. The four examples you give sound like safe assumptions for an interface to make about its implementations. But that depends on what the interface method is accepting as arguments.
For example, if the whole concept of a "host" or a "username" is entirely encapsulated within the implementation (such as hitting a web service from within some kind of service interface, which could just as easily hit a database or some other source of record in other/later implementations) then exceptions about those pieces of data wouldn't make sense at the interface level. It would be better in that case to create an exception type like "DomainException" or "ImplementationException" or "DataRetrievalException" or something like that and just putting the internal implementation details inside of the exception.
To get back to the main point, however... From the perspective of your interfaces and your objects, you shouldn't be concerned with how exceptions are handled by consumers. All you should do is internally handle any exceptions that make sense to internally handle and throw exceptions that make sense to throw. Anything beyond that creates coupling between the consumer and the component.
Although I partly sympathies with your goal of better error handling, I feel that if you forced consumers of your code to handle exceptions correctly then your colleagues would murder you within 20 minutes of checking it in.
Due to C#'s lack of checked exception you're reduced to documenting your code so consumers know what to expect and under what conditions to expect them.
On a side note there is a great plugin for ReSharper called Exceptional that will identify places in your code where you have either not handled a possible exception, or not documented it so callers may do so instead.
精彩评论