Always derive from the Exception class
When creating custom exception classes, always derive from the Exception class, not from the ApplicationException class. One of the reasons for this is that an instance of ApplicationException is thrown only by the application and never by the runtime. Whenever you throw an instance of ApplicationException in your code, you merely increase the call stack without adding much value.
Handle exceptions at the highest level
Note that exceptions are bubbled up to the higher level in the method call hierarchy, and it is not a good practice to handle exceptions in all the layers of your application. You should handle an exception as high up in the call hierarchy as you can. For example, you can consume an exception in the presentation layer and display appropriate messages to the user to communicate the exact error that has occurred.
Use predefined exceptions and clear error messages
It is a good practice to use specific exceptions like FileNotFoundException and IOException when writing exception handlers and then a general catch block at the end with the Exception class. This will ensure that you get a clear understanding of the exact error that has occurred. As Microsoft’s documentation states: “The ApplicationException class does not provide information as to the cause of exceptions. In most scenarios, instances of this class should not be thrown. In cases where this class is instantiated, a human-readable message describing the error should be passed to the constructor.”