You run the risk of swallowing something interesting. This was always the problem with C# exceptions to my mind: there was just one mechanism for reporting errors, which covered everything from the most non-erroneous of events, that aren't even errors, such as file not found or invalid file name encoding or failed to write data to file or socket error during write, to stuff that absolutely should cause the program to go pop and die immediately, such as a null pointer or divide by zero.
Anyway, I sympathize with this thread's OP, as I've had exactly the same problem with C#. In the end, I came to the conclusion you kind of do often have to catch every exception, because you can't trust the functions you call, and the documented exception lists for framework stuff are not always accurate. Safest is to put each call in its own try...catch block, and do any property accesses outside the try...catch block so you find out about the NullReferenceExceptions.
This is annoyingly verbose though.
It also does assume your setters and getters don't throw anything.
I mostly liked C#, but the exceptions aspect is not good. Similarly, I've generally despised working with Go, but the way it deals with errors is not its worst feature! (You do have to pay attention to the linter output though! It's the same err variable every time, so the compiler's fastidious checks for variable use are always foiled...)
> the most non-erroneous of events, that aren't even errors...
You're applying a value judgement that the language makers can't and shouldn't make for you. Whether the error is innocuous or malicious, the happy path of your code cannot proceed and needs explicit handling by the programmer.
No value judgement here, I don't think, at least not from me (though of course my values inform every opinion I have, including this one). I simply claim that every path is equivalent, and all must be considered. I claim there is no so-called "happy path" - a term I reject - nor do "errors" really exist.
There is one case where the socket send succeeded, and another where it failed. There is one case where the file name encoding is valid, and another where it is not. And so on. It makes no sense, in my view, for one case to be handled with one language mechanism and the other case to be handled with another.
By comparison, there is (or so I claim!) no valid path where a null pointer is dereferenced, or where a value is divided by zero. So I will accept the requirement for some other mechanism for dealing with these - though by the time the code is released to the wild, I would hope that all such occurrences would have been eliminated.
The correct behavior for not just the vast majority of these conditions but virtually all of them is the same: propagate the error. You aren't going to sit around and try to "recover" from failing to send a packet or encode a filename: you just report it to higher level code. There might be something you can do at a higher level--such as reconnecting or using a different server from a load balancing list--but at that point the exact reason is irrelevant (and yet should be maintained in case the higher level code needs to inform the user in either a dialog or a log file): failure is, virtually all of the time, boolean in nature and a non-local phenomenon.
My opinion was informed by writing the higher levels rather than the lower ones! Yes, the code absolutely is going to try to recover from sending a packet, because it was sending that in service of some larger goal that now needs unwinding partway through. And, yes, it is going to try to reconnect, or use a different server - or whatever.
The thing it really doesn't want to do is punt the issue on to some higher level. Because there isn't one.
But, equally, you don't want to be swallowing interesting problems that genuinely indicate actual bugs. Those, you do want to pass on (and due to the lack of any higher level, your process will be killed, and some mechanism will spring into action to produce a report).
Anyway, I sympathize with this thread's OP, as I've had exactly the same problem with C#. In the end, I came to the conclusion you kind of do often have to catch every exception, because you can't trust the functions you call, and the documented exception lists for framework stuff are not always accurate. Safest is to put each call in its own try...catch block, and do any property accesses outside the try...catch block so you find out about the NullReferenceExceptions.
This is annoyingly verbose though.
It also does assume your setters and getters don't throw anything.
I mostly liked C#, but the exceptions aspect is not good. Similarly, I've generally despised working with Go, but the way it deals with errors is not its worst feature! (You do have to pay attention to the linter output though! It's the same err variable every time, so the compiler's fastidious checks for variable use are always foiled...)