Many of the other answers are justifying the approach of an existing exception mechanism. That's fine, but it can also be illuminating to compare exceptions to other approaches and ideas.
In particular, consider the stack trace attached to an exception. We can generate a stack trace at any point in a program, and it's common to think of them as the history of the computation (i.e. "how did the program reach this point?"), but that's not quite right. A stack trace only tells us about calls which have not yet returned, i.e. those waiting for the current step to finish. Hence it can be more illuminating to think of a stack trace as the future of a computation (i.e. "what will resume after this step returns?").
In your example, the stack will look something like the following, where each line is a stack frame ('_' indicates where to 'plug in' the return value of the line above):
0
assertNonZero(_) // I've made up the name of this step, for illustration
1/_
_.ToString
MessageBox.Show(_)
Notice that the multiplication, addition and subtraction doesn't appear, since they've already finished. The call stack shows us what remains to happen in the future, which is also called a continuation. The above stack is waiting to perform a non-zero check on the divisor, then perform a division with that checked divisor, then perform a string conversion of that result, and finally show a message box with that string.
Throwing an exception will bypass the usual return mechanism (instead, the stack will be popped until we find a matching catch
handler). In this example, the DivideByZeroException
is thrown by the non-zero check, once it's called with the value 0
. Hence one reason the exception doesn't contain the dividend (the number 1
in this case) is that the exception is thrown before we reached the division step (where the 1
occurs).
As an extreme example, let's imagine we alter your example a little:
MessageBox.Show(("The result is: " + (1/(a*a-(a+a))).ToString()));
This involves another piece of data, the string "The result is: "
. If we want the DivideByZeroException
to contain the dividend of the division step (which would have happened, if the exception were not thrown), do we also want it to contain that string (since we would have prepended it, if the exception were not thrown)? That seems less reasonable, but it highlights the fact they're both just data for steps that the exception prevented from happening.
Note that the stack trace does contain a frame for the division; and the altered version would also contain a frame for the string append. Hence we could argue that including the dividend in a DivideByZeroException
is a poor solution, since it's an ad-hoc decision, it requires co-ordination with the exception's consumers (via the public interface of DivideByZeroException
), and we must face similar decisions every time we implement a new exception type. A more elegant solution would be to stop excluding arguments from the stack trace: that solves the problem for all exceptions, and gives callers a unified interface to access any data (via the frames of the stack trace).
The reason argument data is excluded from stack frames is probably a mixture of performance and encapsulation, and may vary between languages. Interestingly, if arguments were accessible from stack traces, we could implement resumable exceptions, e.g. our exception handler could replace the 0
in the above stack trace with a different number, and calculate what would have happened in that case.
Resumable exceptions aren't a common language feature; however, they are closely related to coroutines, which are becoming popular. In particular, throw
is similar to yield
, try
/catch
are similar to async
/await
, and all of these can be generalised to shift
/reset
(the latter implementing "delimited continuations")
InvalidConstraintException
does not give access to the violated constraint") to the question or doubt ("What's the sense of anInvalidConstraintException
if thatException
does not even give you the possibility to view theConstraint
I have violated?"), which led to this more or less philosophical question "What's the sense of having specific types ofException
classes if they don't give the possibility to address the cause of their own existence?". – Dominique Sep 22 '21 at 06:25ex.violated_Constraint
to allow you to do? – Thorbjørn Ravn Andersen Sep 22 '21 at 07:07DivisionByZeroException
example is a terrible example. The main focus of my question should be the lack ofConstraint
related information in case of anInvalidConstraintException
. – Dominique Sep 22 '21 at 12:10InvalidConstraintException
the way they did", and the only people who can answer that conclusively is them. As they're unlikely to answer here, this almost certainly means that any answer posted here will not be by said people, which implies said answer can only be speculation and/or guesswork i.e. opinion, which means this question is off-topic and should be closed. – Ian Kemp Sep 22 '21 at 14:27var zero = 0; var error = 1 / zero;
whenever I need to throw a quick and dirty exception while testing. Becausethrow new Exception();
just feels like cheating. – Jaquez Sep 22 '21 at 15:31