.NET: Avoid custom exceptions using existing types, but what?

Consider the following code (ASP.NET/C#):

private void Application_Start(object sender, EventArgs e) { if (!SetupHelper.SetUp()) { throw new ShitHitFanException(); } } 

I was never too indecisive to simply collapse my own exception type, mainly because I found (bad practice or not) that basically a reasonable descriptive type name gives us enough so that developers can go through to find out what happened and why something could happen. Sometimes existing .NET exception types even address these needs - regardless of the message.

In this particular scenario, for demonstration purposes only, the application must die from a terrible, shameful death if SetUp not executed properly (according to its return value), but I cannot find an existing exception type in .NET that seems to be will be sufficient; although, I'm sure he will be there, and I just donโ€™t know about it.

Brad Abrams published this article listing some of the available exception types . I say a few because the article has been since 2005, and although I try to keep abreast of the latest developments, this is more than a plausible assumption that more versions are added to future versions of the frameworks that I still donโ€™t know.

Of course, Visual Studio gives you a well-formatted, scrollable list of exceptions through Intellisense, but even with the analysis, I cannot find anything that would seem to be sufficient for this situation ...

ApplicationException: ... when a non-fatal application error

The name seems reasonable, but the error is very definitely fatal - the application is dead.

ExecutionEngineException: ... when there is an internal error in the CLR execution engine

Again, it sounds reasonable, superficial; but this is a very definite goal and to help me here, of course, is not so.

HttpApplicationException: ... when there is an error processing the HTTP request

Well, we run the ASP.NET expression! But we are also just pulling a straw here.

InvalidOperationException: ... when the call is not valid for the current state of the instance

This is wrong, but I add it to the list of "maybe you should put a gun in your head, yes."

OperationCanceledException: ... after canceling the operation, the thread ran

Maybe I donโ€™t feel so bad using this one, but I will still hijack a hell of a lot.

You may even ask why on earth I would like to make an exception here, but the idea is to find out that if I did this , then you know about the corresponding exception for such a scenario? And basically, to what extent can we correspond with .NET in a rational way?

+4
source share
9 answers

It seems that you are trying to deal with a situation where an error occurs from which you simply cannot recover. When I come across these situations, I do my best to fail and fail quickly. I usually avoid reusing a known exception because this is not the situation I want to recover about. I want him to fail.

As I usually approach this problem, this is the use of Contracts.

 private void Application_Start(object sender, EventArgs e) { // If this doesn't work why keep executing??? Contract.Requires(SetupHelper.SetUp()); ... } 

Note. Depending on the limitations of the project, I will either use .Net-contracts from BCL, or my own version

 public static class Contracts { private sealed class ContractException : Exception { ... } public static void Requires(bool value) { if (!value) { throw new ContractException(); } } } 

My personalized solution is inferior to BCL contracts, but is mostly implemented when BCL contracts cannot be used for any reason.

+2
source

I think the problem with choosing the type of exception for your code sample is that the logical result of the .SetUp method gives you exactly zero information about what happened.

I would do one of two things. The first thing would be to change the .SetUp method to return something that gave me an indication of why this failed

Another way is for the .SetUp method itself to throw an exception and make a decision in its Application_Start event whether it allowed to bubble up to the OS or fix any problems and try again.

Basically, when using exceptions, you want the code that makes it be as close to the situation as possible in order to intelligently notify the call stack about what the real problem is.

If for some reason you cannot do this, which means that something did not work, and you have a zero idea, why then I create a new exception with the default message: "An error occurred in {0}", where {0} is a module or method to at least give me an idea of โ€‹โ€‹where to start looking.

By the way, fatal usually means a common CLR crash, which is slightly different from what your application cannot process based on its state.

+2
source

I believe that using my own exceptions gives me more control over problems.

 private void Application_Start(object sender, EventArgs e) { try { SetupHelper.SetUp() //Awesome } catch(InvalidDisplaySettingsException ex) { //Do Something based on the type of the exception } catch(DatabaseConfigurationException ex) { //Do something based on the type of the exception } catch(Exception ex) { //Now sh.. hit the fan } } 

There are no built-in .NET exceptions in most cases. There are very few general exceptions, and the rest are specific to the fact that they were created so that the standard programmer would not want to use them in his application. I like to create my own exceptions, because I can add more information and be able to do a few catches.

+1
source

Is it just a matter of cleanliness or do you have a reporting infrastructure that you want to see in a specific exception? Obviously, if you do not plan to catch the exception, then its type (practically speaking) does not matter.

Depending on why ShitHitTheFan then I would say that InvalidOperationException or ApplicationException make sense enough. Adding a detailed message seems sufficient to explain the reason. I tend to be pragmatic, so the fact that the documentation indicates that ApplicationExceptions is not fatal does not matter to me. Any exception can be fatal if it is not caught ...

Depending on any failure / log report structure that can be used, it may also be useful to populate the Exception.Data property with status information to help you debug the problem.

Edit: Also, if you send a special exception route, please use the constructors needed for serialization. You never know the months / years in the future when your exception can be sent by wire.

+1
source

You need to read these articles on MSDN:

For your sample code, throwing an InvalidOperationException is most appropriate: if the initialization failed, you are probably in an "invalid state".

You should not throw instances of SystemException or ApplicationException : at first, constructive vanity was a hierarchy with System.Exception at the top of the heap. It has two children, SystemException and ApplicationException , from which all other exceptions will be thrown.

SystemException should have been the root of system exceptions (e.g. CLR) and ApplicationException root of exceptions defined by the user / application. They found that in practice this did not bring much benefit: from the article "Best Practices" above:

For most applications, throw custom exceptions from the Exception class. It was originally intended that custom exceptions should be thrown from the ApplicationException class; however, in practice this has not been found to add significant value.

If you are going to write custom exceptions, you must execute correctly. If it is likely that an exception will be thrown across the boundaries of the application domain (for example, a cross process or a cross machine), consider associating your special exceptions with the individual assemblies that are indicated on both sides of the fence. You will get ... interesting ... problems when an exception of an unknown type is caught.

When implementing a custom exception, you need to provide at least 3 standard public constructors plus the protected constructor and method necessary for serialization / deserialization, although the constructor and method related to serialization can be omitted if you have not actually extended the base class. Here is an example of the minimum scaffolding required for a reliable custom exception:

 using System; using System.Runtime.Serialization; using System.Security.Permissions; namespace sandbox { /// <summary> /// <para> /// Minimum scaffolding required for a custom exception. /// </para> /// <para> /// Consider providing custom properties/fields to provide context and semantic details /// regarding the exception instance. Alternatively, use the dictionary provided by the /// base class for that purpose. See <see cref="System.Exception.Data"/> for more details. /// </para> /// </summary> [Serializable] class MyCustomException : Exception { #region public constructors public MyCustomException() : base() { // set Message to the default localized message for your exception here // The base constructor for System.Exception sets InnerException to null as well as setting a default message. return ; } public MyCustomException( string message ) : base( message ) { return ; } public MyCustomException( string message , Exception innerException ) : base( message , innerException ) { return ; } #endregion public constructors #region serialization-related constructor/method /// <summary> /// protected constructor. Used for deserialization /// </summary> /// <param name="info"></param> /// <param name="context"></param> protected MyCustomException( SerializationInfo info , StreamingContext context ) : base( info , context ) { // do other deserialization-related initialization here (eg set your object properties from info and context). return ; } /// <summary> /// Perform custom serialization /// </summary> /// <param name="info"></param> /// <param name="context"></param> [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)] public override void GetObjectData( SerializationInfo info, StreamingContext context ) { // do any serialization work required by your custom exception here base.GetObjectData( info, context ); return ; } #endregion serialization-related constructor/method } } 
+1
source

Invalidoperationexception seems appropriate, as it is intended to warn of something that went wrong and cannot be repaired.

0
source

I would consider most of the .net exceptions, which would be relatively unhelpful to get out of them in any circumstances where you might expect to be caught in anything other than a catch situation. The problem is that even if the state of your object can be known in all cases when you throw a TimeoutException, this does not mean that the state of your object is known if any code that you throw throws a TimeoutException by simply giving him to bubble on the stack.

I would suggest that it is better to have more different types of exceptions than to use one type of exception for several different purposes. If copying an exception template is annoying, here's the trick: determine the type of exception with one or more typical parameters. Note that MisterDissapointedException <DerivedType> will not inherit from MisterDissapointedException <Corresponding BaseType>, but you can define a MisterDissapointedException <T, U> that inherits from MisterDissapointedException <T>.

If you are following this route, there is no need to copy the template for each new exception that you want. Just pass a class parameter that is not used for any other MisterDissapointedException.

0
source

I would InvalidOperationException . Since this exception is not intended to be captured, you do not need to worry about the type, the message is important. I would also throw it in the SetupUp method instead of returning a Boolean .

0
source

Your problem is that the Setup method uses the return value instead of an exception for the error message.

If you simply allow an exception from the installation program implementation, it will be easier to see what type of exception should be thrown, for example.

  • ConfigurationErrorsException if the installation failed due to missing or incorrect configuration information in the application configuration file.

  • SqlException if the command failed to access the SQL Server database during installation.

  • ... etc.

0
source

Source: https://habr.com/ru/post/1344297/


All Articles