. NET Exception design principles

Source: Internet
Author: User
Tags sql server error codes stack trace unpack
Exceptions are used. NET, however, there are too many developers who do not consider this issue from an API design standpoint. In most jobs, they always know what exceptions to catch and which exceptions need to be written to the global log. If you design an API that allows you to use the exception correctly, you can significantly reduce the time it takes to fix the bug.

Who's wrong?

The basic theory behind exception design begins with the question, "Who's wrong?" "To facilitate this discussion, the answer to this question will always be one of the following three:

Library

Application

Environment

When we say "library" has a problem, we mean that there is an internal flaw in a method that is currently being executed. In this case, "application" is the code that calls the library method (this is a bit tricky because the library and application code might be in the same assembly.) Finally, "environment" means anything beyond the application that cannot be controlled.

Library defects

The most typical library flaw is NullReferenceException. For the library, it has no reason to throw a null reference exception that can be detected by the application. If an empty is encountered, the library code should always throw a more specific exception, stating what is empty and how to correct the problem. For parameters, this is obviously a ArgumentNullException exception. InvalidOperationException is usually more appropriate if the property or field is empty.

By definition, any exception that indicates a library defect is a bug that needs to be fixed in the library. That is not to say that the application code does not have a bug, but that the library bug needs to be repaired first. Only then can the application developer know that he has made a mistake.

The reason for this is that there may be many people using the same library. If a person mistakenly passes in empty places where they should not be passed in, the other person must have made the same mistake. Replace the NullReferenceException with an exception that clearly shows what went wrong, and the application developer can immediately know what is wrong.

"The nucleus of success (the Pit of Success)"

If you've read about. NET design patterns, you will often encounter the phrase "the nucleus of success". The basic idea is this: make the code easy to use correctly, not easily misused, and make sure that the exception tells you what went wrong. By following this API design concept, it is almost guaranteed that developers will write the right code from the start.

That's why a NullReferenceException with no comments is so bad. In addition to stack traces (which may be very deep into the library code), there is no information to help developers determine what they did wrong. ArgumentNullException and InvalidOperationException, on the other hand, provide a way for library authors to explain to the application developers how to fix the problem.

Other library defects

The next library flaw is the ArithmeticException series, including DivideByZeroException, Finitenumberexception, and OverflowException. Again, this always means the internal flaw of the library method, even if that flaw is only a missing parameter validity check.

Another example of a library flaw is indexoutofrangeexception. Semantically, it's no different from ArgumentOutOfRangeException, see Ilist.item, but it only works with array indexers. Because the application code typically does not use a bare array, this means that the custom collection class will have a bug.

Since the introduction of the generic list from. NET 2.0, ArrayTypeMismatchException is rare. The situation that triggered the exception was rather bizarre. According to the documentation:

ArrayTypeMismatchException is thrown when the system cannot convert an array element to a declared array type. For example, an element of type string cannot be stored in an Int32 array because the two types cannot be converted. Applications generally do not need to throw such exceptions.

To do this, the previously mentioned Int32 array must be stored in a variable of type object[]. If you use the original array, the library needs to check this. For this reason and many other considerations, it is best not to use the original arrays, but to encapsulate them in a suitable collection class.

In general, other conversion problems are reflected by InvalidCastException exceptions. Back to our topic, type checking should mean never throwing an InvalidCastException exception, but throwing a argumentexception or InvalidOperationException exception to the caller.

Memberaccessexception is a base class that covers a variety of reflection-based errors. In addition to using reflection directly, the exception is triggered by improper use of COM interop and Dynamic keywords.

Application defects

A typical application flaw is ArgumentException and its subclasses ArgumentNullException and ArgumentOutOfRangeException. Here are some other subcategories you may not know about:

    • System.ComponentModel.InvalidAsynchronousStateException

    • System.ComponentModel.InvalidEnumArgumentException

    • System.duplicatewaitobjectexception

    • System.Globalization.CultureNotFoundException

    • System.IO.Log.ReservationNotFoundException

    • System.Text.DecoderFallbackException

    • System.Text.EncoderFallbackException

All of this is a clear indication of an application error, and the problem is in the line that calls the library method. The two parts of that statement are important. Consider the following code:

Foo. Customer = Null;foo. Save ();

If the above code throws a ArgumentNullException exception, then the application developer will be confused. It should throw a InvalidOperationException exception that shows what went wrong before the current line.

Take exception as document

A typical programmer does not read a document, at least not the first. Instead, he or she will read the public API, write some code and run it. If the code does not work correctly, search for exception information on the stack overflow. If the programmer is lucky, it's easy to find the answer and link to the correct document. But even so, programmers will probably not actually read it.

So, as a library author, how do we solve this problem? The first step is to copy part of the document directly into the exception.

More Object state Exceptions

InvalidOperationException has a well-known sub-class ObjectDisposedException. Its use is obvious, however, few of the classes that can be destroyed will forget to throw the exception. If you forget, the common result is to throw a NullReferenceException exception. This exception is caused by the Dispose method, which causes the destruction of child objects to be empty.

Closely related to InvalidOperationException is the NotSupportedException anomaly. These two exceptions are easily distinguishable: InvalidOperationException means "You can't do that now", and NotSupportedException means "you can never do that kind of thing with this class." Theoretically, NotSupportedException should only appear when using an abstract interface.

For example, an immutable collection should throw a NotSupportedException exception when encountering the Ilist.add method. In contrast, a frozen collection throws an InvalidOperationException exception when it encounters the method in a frozen state.

NotSupportedException an increasingly important subclass is platformnotsupportedexception. The exception indicates that the operation can be performed in certain operating environments, but not in other environments. For example, when you add code from the. NET porting to the UWP or. NET core, you might want to use this exception because they do not provide all the features of the. NET Framework.

The Elusive FormatException

Microsoft in the design. NET and made some mistakes in the first version. For example, FormatException is logically a parameter exception type, and even the document says "This exception is thrown when the parameter format is invalid." However, for whatever reason, it does not actually inherit ArgumentException. It also has no place to store parameter names.

Our proposal for the moment is not to throw a FormatException exception, but to create a subclass of ArgumentException that you can name "Argumentformatexception" or other similar effects. This can provide you with the necessary information, such as parameter names and actual values used, to reduce debugging time.

This brings us back to the original theme "Exception design". Yes, when your self-developed parser detects a problem, you can just throw a FormatException exception, but that doesn't help the application developer who wants to use your library.

Another example of this framework design flaw is indexoutofrangeexception. Semantically, it's no different from ArgumentOutOfRangeException, but is this exception only for array indexers? No, it's wrong to think that way. Looking at the Ilist.item instance set, the method throws only the ArgumentOutOfRangeException exception.

Environmental deficiencies

Environmental deficiencies originate from the fact that the world is not perfect, such as data outages, Web server unresponsive, file loss scenarios. When an environmental defect occurs in a bug report, the following two aspects need to be considered:

Does the application handle the defect correctly?

What is the cause of the defect in this environment?

Typically, this involves a division of labor. First, the application developer should be the first to find the answer to a question. This is not just about handling errors and recovering, but also generating a useful log.

You might want to know why you should start with an application developer. Application developers are responsible for the operations team. If a Web server call fails, the application developer cannot simply shout "It's not my problem". He or she first needs to make sure that the anomalies provide enough detail to enable OPS to carry out their work. If the exception provides only the "Server connection timeout" information, how can they know which server is involved?

Private exception

NotImplementedException

NotImplementedException represents and represents only one thing: This feature is still in the process of development. Therefore, the information provided by NotImplementedException should always contain a reference to a task tracking software. For example:

throw new NotImplementedException ("See Work Order #42.");

You can provide more detailed information, but in fact, any information you record will expire almost immediately. Therefore, it is better to just direct readers to work orders, where they can see information such as when the feature will be implemented as planned.

AggregateException

AggregateException is a necessary evil, but it is difficult to use. It does not contain any valuable information in its own right, and all details are hidden in its InnerExceptions collection.

Since aggregateexception usually contains only one item, it seems logical to unpack it in the library and return the real exception. In general, you cannot throw an inner exception again without destroying the original stack trace, but starting with. NET 4.5, the framework provides a way to use Exceptiondispatchinfo.

Solution Package AggregateException

catch (AggregateException ex) {      if (ex. Innerexceptions.count = = 1)//unpack               exceptiondispatchinfo.capture (ex. Innerexceptions[0]). Throw ();    else          throw;//We really need aggregateexception}

A situation that cannot be answered

There are some exceptions that cannot be simply included in this topic. For example, accessviolationexception indicates that there is a problem reading unmanaged memory. Yes, that could be caused by the native library code, or by the application using the same code base incorrectly. Only through research can we reveal the nature of this bug.

If possible, you should avoid the inability to answer the exceptions at design time. In some cases, the static code parser of Visual Studio can even parse the identity conflicts covered by the rule.

For example, ApplicationException is actually obsolete. The Framework Design Guide clearly states, "do not throw or inherit applicationexception. "To do this, the application does not have to throw applicationexception exceptions. Although this is the intention, but look at the following sub-categories:

    • Microsoft.JScript.BreakOutOfFinally

    • Microsoft.JScript.ContinueOutOfFinally

    • Microsoft.JScript.JScriptException

    • Microsoft.JScript.NoContextException

    • Microsoft.JScript.ReturnOutOfFinally

    • System.Reflection.InvalidFilterCriteriaException

    • System.Reflection.TargetException

    • System.Reflection.TargetInvocationException

    • System.Reflection.TargetParameterCountException

    • System.Threading.WaitHandleCannotBeOpenedException

Obviously, some of these subclasses should be parameter exceptions, while others represent environmental issues. They are all not "application exceptions" because they are only thrown by the. NET Framework's libraries.

By the same token, developers should not use SystemException directly. As with ApplicationException, SystemException subclasses are also different, including ArgumentException, NullReferenceException and AccessViolationException. Microsoft even suggested forgetting the existence of systemexception and using only its subclasses.

The inability to answer has a subcategory of infrastructure anomalies. We've seen AccessViolationException, and the following are other infrastructure anomalies:

    • CannotUnloadAppDomainException

    • BadImageFormatException

    • Datamisalignedexception

    • TypeLoadException

    • TypeUnloadedException

These exceptions are often difficult to diagnose and may reveal difficult-to-understand bugs in the library or code that invokes it. Therefore, unlike applicationexception, it is reasonable to classify them as impossible to answer.

Practice: Redesign SqlException

Please keep these principles in mind and let's look at the SqlException. In addition to network errors (you simply cannot reach the server), there are more than 11,000 different error codes in the Master.dbo.sysmessages table in SQL Server. So, while the exception contains all the underlying information you need, in addition to simply capturing & recording, you actually have a hard time doing anything.

If we were to redesign SqlException, we would want to break it down into different categories based on what we expect the user or developer to do.

    • Sqlclient.networkexception will represent all error codes that indicate problems with the environment outside of the database server itself.

    • The sqlclient.internalexception contains an error code that indicates a serious failure of the server, such as a database corruption or inaccessible hard disk.

    • Sqlclient.syntaxexception is the equivalent of our ArgumentException. It means that you pass bad SQL to the server (either directly or because of an ORM bug).

    • Sqlclient.missingobjectexception occurs when the syntax is correct but the database objects (tables, views, stored procedures, and so on) do not exist.

    • Sqlclient.deadlockexception occurs when two or more processes attempt to modify the same information when a conflict arises.

Each of these anomalies implies a plan of action.

    • Sqlclient.networkexception: Retry the operation. If this occurs frequently, contact your operations personnel.

    • Sqlclient.internalexception: Contact DBA now.

    • Sqlclient.syntaxexception: Notifies the application or database developer.

    • Sqlclient.missingobjectexception: Ask the OPS person to check if the last database deployment was lost.

    • Sqlclient.deadlockexception: Retry the operation. If this occurs frequently, a design error is found.

If we were to do this in the actual work, then we had to map all of the more than 11,000 SQL Server error codes to one of those categories, which is a particularly daunting task, which explains why SqlException is the way it is.


Summarize

When designing an API, to make it easier to correct the problem, organize the exception according to the type of action you need to perform. This makes it easier to write out-of-school code, record more accurate logs, and quickly communicate problems to the right people or teams.


About the author

Jonathan Allen began to take part in the MIS project for the infirmary in the late 90 and gradually promoted them from Access and Excel to an enterprise-class solution. He spent five years writing automated trading systems in the financial industry and then decided to turn to high-end user interface development. In his spare time, he enjoys learning Western fighting skills from 15 to 17th century and writing about it.

The above is. NET exception design principles content, more relevant content please pay attention to topic.alibabacloud.com (www.php.cn)!

  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.