...
/Throwing and Catching Exceptions in Functions
Throwing and Catching Exceptions in Functions
Learn about exception handling, utilize built-in exception types and implement strategies like rethrowing and the tester-doer pattern.
We should catch and handle an exception if we have enough information to mitigate the issue. If we do not, then we should allow the exception to pass up through the call stack to a higher level.
Understanding usage errors and execution errors
Usage errors are when a programmer misuses a function, typically by passing invalid values as parameters. A programmer could avoid them by changing their code to pass valid values. When some programmers first learn C# and .NET, they sometimes think exceptions can always be avoided because they assume all errors are usage errors. Usage errors should all be fixed before production runtime.
Execution errors are when something happens at runtime that cannot be fixed by writing “better” code. Execution errors can be split into:
- Program errors: If we attempt to open a file that does not exist, we might be able to catch that error and handle it programmatically by creating a new file. Program errors can be programmatically fixed by writing smart code.
- System errors: System errors often cannot be fixed programmatically. If we attempt to access a network resource, but the network is down, we need to be able to handle that system error by logging an exception, possibly backing off for a time, and trying again. However, some system errors, such as running out of memory, simply cannot be handled.
Commonly thrown exceptions in functions
We should rarely define new types of exceptions to indicate usage errors. .NET already defines many that we should use. When defining our functions with parameters, our code should check the parameter values and throw exceptions if they have values that will prevent our function from properly functioning.
For example, if an argument to a function should not be null, throw ArgumentNullException
. For other problems, we might throw ArgumentException
, NotSupportedException
, or InvalidOperationException
. For any exception, include a message that describes the problem for whoever will have to read it (typically a developer audience for class libraries and functions or end users if it is at the highest level of a GUI app), as shown in the following code:
static void Withdraw(string accountName, decimal amount){if (accountName is null){throw new ArgumentNullException(paramName: nameof(accountName));}if (amount < 0){throw new ArgumentException(message: $"{nameof(amount)} cannot be less than zero.");}// process parameters}
Note: If a ...