Valid C # principle 44: Create an applicationProgramSpecific exception classes
Item 44: Create complete application-specific exception classes
An exception is a mechanism for reporting errors. It can handle errors away from errors. All information about the error must be included in the exception object. When an error occurs, you may want to convert the underlying error into a detailed application error without losing any information about the error. You need to carefully consider how to create special exception classes in the C # application. The first step is to understand when and why to create a new exception class, and how to construct inherited exception information. When developers use your library to write catch statements, they differ in the same behavior based on exceptions during special periods. Different exception classes can be processed differently:
Try {
Foo ();
Bar ();
} Catch (myfirstapplicationexception E1)
{
Fixproblem (E1 );
} Catch (anotherapplicationexception E2)
{
Reporterrorandcontinue (E2 );
} Catch (yetanotherapplicationexception E3)
{
Reporterrorandshutdown (E3 );
} Catch (exception E)
{
Reportgenericerror (E );
}
Finally
{
Cleanupresources ();
}
Different catch statements can exist due to different runtime exceptions. You, as the author of the database, must create or use different exception classes to handle different things in an exception catch statement. If this is not the case, your users have only one boring choice. When an exception is thrown, You can suspend or stop the application. This is of course the least work, but it is impossible to win reputation from users. Or they can get an exception and try to determine whether the error can be corrected:
Try {
Foo ();
Bar ();
} Catch (exception E)
{
Switch (E. targetsite. Name)
{
Case "foo ":
Fixproblem (E );
Break;
Case "bar ":
Reporterrorandcontinue (E );
Break;
// Some routine called by foo or bar:
Default:
Reporterrorandshutdown (E );
Break;
}
} Finally
{
Cleanupresources ();
}
This is far less attractive than using multiple catch statements, which is very fragile.Code: If the name is modified normally, it will be destroyed. If you move the function call that causes the error and put it in a shared tool function, it will also be destroyed. If an exception occurs on a deeper stack, the structure becomes more vulnerable.
Before discussing this topic in depth, let me first describe two things that cannot be promised. First, the exception cannot handle all the exceptions you encounter. This is not a solid guidance method, but I like to throw exceptions for error conditions. If these conditions are not handled immediately or reported, more serious problems may occur in the future. For example, a data integrity error in the database should produce an exception. This problem becomes more serious if it is ignored. However, when writing data to the user's window location fails, it is not as if a series of problems will occur later. It is enough to return an error code to indicate failure.
Second, writing a throw statement does not mean that a new exception class will be created at this time. I recommend that you create more exceptions, rather than just a few common natural exceptions: It seems that when someone throws an exception, it only takes a few minutes to get rid of system. Exception. Unfortunately, only the minimum help information can be provided to process the calling code. On the contrary, consider creating some necessary exception classes to let the calling code understand what the situation is and provide the best opportunity to restore it.
Again: in fact, we need to create different exception classes, and the only reason is that it is easier for your users to write catch statements to handle errors. View and analyze these error conditions to see which types of errors can be put into a recoverable behavior, and then create a specified exception class to handle these behaviors. Can your application be recovered from a file or directory loss error? Can it be recovered from insufficient security permissions? What if network resources are lost? If you encounter different errors and adopt different recovery mechanisms, you should create new exception classes for different behaviors.
Therefore, now you should create your own exception class. When you create an exception class, you have many responsibilities to complete. You should always derive your exception class from the system. applicationexception class, instead of the system. exception class. You do not need to add too many features to this base class. Different exception classes can be processed in different catch statements.
But do not delete anything from the exception class. The applicationexception class has four different constructors:
// Default constructor
Public applicationexception ();
// Create with a message.
Public applicationexception (string );
// Create with a message and an inner exception.
Public applicationexception (string, exception );
// Create from an input stream.
Protected applicationexception (
Serializationinfo, streamingcontext );
When you create a new exception class, you should create this four constructor. Different constructor methods are called to construct an exception in different situations. You can delegate this work to the base class for implementation:
Public class myassemblyexception:
Applicationexception
{
Public myassemblyexception ():
Base ()
{
}
Public myassemblyexception (string S ):
Base (s)
{
}
Public myassemblyexception (string s,
Exception E ):
Base (S, E)
{
}
Protected myassemblyexception (
Serializationinfo info, streamingcontext cxt ):
Base (Info, cxt)
{
}
}
The exception parameter required by the constructor is worth discussing. Sometimes, an exception occurs in one of the class libraries you use. Calling the code of your database may obtain the minimum information about possible behavior correction. When you upload parameters from your exception:
Public double dosomework ()
{
// This might throw an exception defined
// In the Third Party Library:
Return thirdpartylibrary. importantroutine ();
}
When you create an exception, you should provide your own library information. Throw your own detailed exception and include the source exception as its internal exception attribute. You can provide the most additional information you can provide:
Public double dosomework ()
{
Try {
// This might throw an exception defined
// In the Third Party Library:
Return thirdpartylibrary. importantroutine ();
} Catch (exception E)
{
String MSG =
String. Format ("problem with {0} using library ",
This. tostring ());
Throw new doingsomeworkexception (MSG, e );
}
}
}
This new version will create more information where the problem occurs. When you have created an appropriate tostring () method (see Principle 5), you have created an exception object that can fully describe the problem. More, an inline exception shows the root cause of the problem: some information in the third-party library you are using.
This technology is called exception conversion, which converts a bottom layer exception to a more advanced exception. This provides more information about the error. The more information you create about an error, the easier it is to use it for diagnosis and possibly error correction. By creating your own exception classes, you may convert the underlying problems to detailed exceptions. This exception contains detailed application information, this helps you diagnose programs and correct problems as much as possible.
I hope your application does not throw exceptions frequently, but it will happen. If you do not perform any detailed processing, your application may generate a default. NET Framework exception, no matter what error occurs in the method you call. Providing more detailed information will help you and your users diagnose programs and possible correction errors in actual applications. You should create different exception classes only when there are different actions to handle errors. You can create a fully functional exception class by providing constructors supported by all base classes. You can also use the innerexception attribute to carry all error messages of the underlying error conditions.
============================================
Item 44: Create complete application-specific exception classes
Exceptions are the mechanisms of reporting errors that might be handled at a location far removed from the location where the error occurred. all the information about the error's cause must be contained in the exception object. along the way, you might want to translate a low-level error to more of an application-specific error, without losing any information about the original error. you need to be very thoughtful about when you create your own specific exception classes in your C # applications.
The first step is to understand when and why to create new exception classes, and how to construct informative exception hierarchies. when developers using your libraries write catch clses, they differentiate actions based on the specific runtime type of the exception. each different exception class can have a different set of actions taken:
Try {
Foo ();
Bar ();
} Catch (myfirstapplicationexception E1)
{
Fixproblem (E1 );
} Catch (anotherapplicationexception E2)
{
Reporterrorandcontinue (E2 );
} Catch (yetanotherapplicationexception E3)
{
Reporterrorandshutdown (E3 );
} Catch (exception E)
{
Reportgenericerror (E );
}
Finally
{
Cleanupresources ();
}
different catch clses can exist for different runtime types of exceptions. you, as a library author, must create or use different exception classes when catch clses might take different actions. if you don't, your users are left with only unappealing options. you can punt and terminate the application whenever an exception gets thrown. that's certainly less work, but it won't win kudos from users. or, they can reach into the exception to try to determine whether the error can be corrected:
Try {
Foo ();
Bar ();
} Catch (exception E)
{
Switch (E. targetsite. Name)
{
Case "foo ":
Fixproblem (E );
Break;
Case "bar ":
Reporterrorandcontinue (E );
Break;
// Some routine called by foo or bar:
Default:
Reporterrorandshutdown (E );
Break;
}
} Finally
{
Cleanupresources ();
}
That's far less appealing than using multiple catch clures. it's very brittle code: If you change the name of a routine, it's broken. if you move the error-generating callinto a shared utility function, it's broken. the deeper into the call stack that an exception is generated, the more fragile this kind of construct becomes.
Before going any deeper into this topic, let me add two disclaimers. first, exceptions are not for every error condition you encounter. there are no firm guidelines, but I prefer throwing exceptions for error conditions that cause long-lasting problems if they are not handled or reported immediately. for example, data integrity errors in a database shocould generate an exception. the problem only gets bigger if it is ignored. failure to correctly write the user's window location preferences is not likely to cause far-reaching consequences. A return code indicating the failure is sufficient.
second, writing a throw statement does not mean it's time to create a new exception class. my recommendation on creating more rather than fewer exception classes comes from normal human nature: people seem to gravitate to overusing system. exception anytime they throw an exception. that provides the least amount of helpful information to the calling code. instead, think through and create the necessary exceptions classes to enable calling code to understand the cause and provide the best chance of recovery.
I'll say it again: the reason for different exception classesin fact, the only reasonis to make it easier to take different actions when your users write catch handlers. look for those error conditions that might be candidates for some kind of recovery action, and create specific exception classes to handle those actions. can your application recover from missing files and directories? Can it recover from inadequate security privileges? What about missing network resources? Create new exception classes when you encounter errors that might lead to different actions and recovery mechanisms.
So now you are creating your own exception classes. you do have very specific responsibilities when you create a new exception class. you shoshould always derive your exception classes from the system. applicationexception class, not the system. exception class. you will rarely add capabilities to this base class. the purpose of different exception classes is to have the capability to differentiate the cause of errors in catch clses.
But don't take anything away from the exception classes you create, either. The applicationexception class contains four constructors:
// Default constructor
Public applicationexception ();
// Create with a message.
Public applicationexception (string );
// Create with a message and an inner exception.
Public applicationexception (string, exception );
// Create from an input stream.
Protected applicationexception (
Serializationinfo, streamingcontext );
When you create a new exception class, create all four of these constructors. Different situations call for the different methods of constructing exceptions. You delegate the work to the base class implementation:
Public class myassemblyexception:
Applicationexception
{
Public myassemblyexception ():
Base ()
{
}
Public myassemblyexception (string S ):
Base (s)
{
}
Public myassemblyexception (string s,
Exception E ):
Base (S, E)
{
}
Protected myassemblyexception (
Serializationinfo info, streamingcontext cxt ):
Base (Info, cxt)
{
}
}
The constructors that take an exception parameter deserve a bit more discussion. sometimes, one of the libraries you use generates an exception. the code that called your library will get minimal information about the possible corrective actions when you simply pass on the specified tions from the utilities you use:
Public double dosomework ()
{
// This might throw an exception defined
// In the Third Party Library:
Return thirdpartylibrary. importantroutine ();
}
You shoshould provide your own Library's Information when you generate the exception. throw your own specific exception, and include the original exception as its innerexception property. you can provide as much extra information as you can generate:
Public double dosomework ()
{
Try {
// This might throw an exception defined
// In the Third Party Library:
Return thirdpartylibrary. importantroutine ();
} Catch (exception E)
{
String MSG =
String. Format ("problem with {0} using library ",
This. tostring ());
Throw new doingsomeworkexception (MSG, e );
}
}
}
This new version creates more information at the point where the problem is generated. as long as you have created a proper tostring () method (see item 5), you 've created an exception that describes the complete state of the object that generated the problem. more than that, the inner exception shows the root cause of the problem: something in the third-party library you used.
This technique is called exception translation, translating a low-level exception into a more high-level exception that provides more context about the error. the more information you generate when an error occurs, the easier it will be for users to diagnose and possibly correct the error. by creating your own exception types, you can translate low-level generic problems into specific exceptions that contain all the application-specific information that you need to fully diagnose and possibly correct the problem.
your application will throw exceptionshopefully not often, but it will happen. if you don't do anything specific, your application will generate the default. NET framework exceptions whenever something goes wrong in the methods you call on the core framework. providing more detailed information will go a long way to enabling you and your users to diagnose and possibly correct errors in the field. you create different exception classes when different corrective actions are possible and only when different actions are possible. you create full-featured exception classes by providing all the constructors that the base exception class supports. you use the innerexception property to carry along all the error information generated by lower-level error conditions.