When creating a custom Exception type in C#, why is it considered to be good practice to overload all of those constructors:
Exception()
Exception(String)
Exception(String, Exception)
See: https://learn.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions#include-three-constructors-in-custom-exception-classes
I see no given reason for that in any of those recommendations.
If I want to create a custom Exception which shall be thrown if an unknown error occurs calling another system, why is it considered to be good to overwrite the other constructors here, if I just need information about the failed system and the operation that lead to it:
public class ExternalSystemCallException : Exception {
public string FailedSystem { get; }
public string OperationName { get; }
public ExternalSystemCallFailedException(string failedSystem, string operationName) {
FailedSystem = failedSystem;
OperationName = operationName;
}
}
Disclaimer: I know there could be other information I could pass here, so this is just a rather simple example.
UPDATE 1:
So as I understand it, I shall overwrite all constructors but add all of the parameters the Exception needs additionally as well. Is that correct?
Example:
Exception(string failedSystem, string operationName)
: base()
Exception(string failedSystem, string operationName, string message)
: base(message)
Exception(string failedSystem, string operationName, string message, Exception innerException)
: base(message, innerException)
Because both Message and InnerException properties are readonly on Exception class. That means the only way to set them is via constructor. Your implementation does not allow to set those properties, because you omit related constructors. That means your exception:
Loses valuable information contained in another exception which lead to it (if any). It's InnerException is always null.
Display no human readable information when logged or displayed to user. When exception is logged - only properties of base Exception class are taken into account, such as Message and InnerException. Those properties are always null for your exception, so only stack trace (and even that is not complete) will appear in log (your FailedSystem and OperationName will not appear there too).
You might think that users of your code will catch specific ExternalSystemCallException exception and then act based on its properties, but that's not what often happens. Your code might be used as a part of larger operation, then there will be some catch-all handler up the stack which will just log exception and show some error message to the user. So having base Exception properties set to meaningful values is imporatant.
To "fix" your exception type you might consider doing something like this:
public class ExternalSystemCallException : Exception
{
public string FailedSystem { get; }
public string OperationName { get; }
public ExternalSystemCallException(
string failedSystem,
string operationName,
Exception innerException = null)
: base($"Operation {operationName} failed in {failedSystem}", innerException) {
FailedSystem = failedSystem;
OperationName = operationName;
}
}
That way you always set Message to meaningful value and allow to pass InnerException if needed. Empty Exception constructor can be omited if it never makes sense to throw exception of your type without providing system and operation name values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With