. NET exception handling Best Practices (translated)

Source: Internet
Author: User
Tags finally block goto stack trace throw exception

This article is translated from an article on CodeProject, the original address.

Directory

    • Introduced
    • To prepare for the worst.
      • Check in advance
      • Do not trust external data
      • Trusted devices: Camera, mouse, and keyboard
      • "Write operations" may also fail
    • Secure programming
      • Do not throw "new Exception ()"
      • Do not store important exception information in the message property
      • Each thread must contain a try/catch block
      • To record after catching an exception
      • Do not log only the value of exception.message, you need to record exception.tostring ()
      • To catch a specific exception
      • Do not abort the exception on the throw
      • The cleanup code is placed in the finally block
      • Don't forget to use the using
      • Do not use special return values to represent exceptions that occur in a method
      • Do not use the "throw exception" method to indicate that the resource does not exist
      • Do not use "throw exception" as a result of function execution
      • You can use the "throw exception" method to highlight errors that cannot be ignored
      • Do not empty the stack trace information
      • The exception class should be marked as Serializable
      • Use "Throw exception" instead of Debug.Assert
      • Each exception class consists of at least three construction methods
    • Don't reinvent the wheel.
    • vb.net
      • Simulating a using statement in C #
      • Do not use unstructured exception handling (on Error goto)
    • Summarize

Introduced

"My software program never goes wrong." Do you believe that? I can almost be sure that everyone will yell I'm a liar. "Software programs are almost impossible without bug! ”

In fact, it's not impossible to develop a trustworthy, robust software program. Note that I am not referring to the software that is used to control nuclear power plants, but to some common commercial software that may run on the server or on a PC that can work for weeks or even months without major problems. As you can guess, I just meant that the software has a low error rate, you can quickly find the cause of the error and quickly fix it, and the error will not cause significant data corruption.

In other words, I mean the software is relatively stable.

It is understandable that bugs are available in the software. But if it is a frequent bug, and because there is not enough information to prompt you to fix it quickly, then this situation is unforgivable.

To get a better understanding of what I've said, let me give you an example: I've often seen countless commercial software giving such error hints when encountering hard drives:

"Failed to update customer profile, please contact your system administrator and try again".

In addition to these, no other information is recorded. Figuring out what causes this error is a time-consuming process, and the programmer may need to make a variety of guesses before actually finding the cause of the problem.

Notice in this article, I mainly talk about how to better deal with. NET programming is not intended to discuss how to display the appropriate "error message" because I think the job belongs to the UI developer and most of it depends on the type of UI interface and the users who end up using the software. For example, an ordinary user-oriented text editor "error message" should be completely different from a socket communication framework, because the latter is the direct user of the programmer.

To prepare for the worst.

Adhering to some basic design principles can make your program more robust and improve the user experience when errors occur. The "Improve user experience" I'm talking about does not mean that the wrong hint form will delight the user, but that the error will not damage the original data and will not crash the entire computer. If your program encounters an insufficient hard drive error, but the program does not cause any other negative effects (just the error message, no other problems, the translator), then the user experience is enhanced.

    • Check in advance

Strong type checking and validation is a powerful way to prevent bugs from occurring. The sooner you find the problem, the sooner it will fix the problem. After a few months, I want to figure out "Why is there a CustomerID data in the ProductID column in the Invoiceitems table?" "It's not easy and it's pretty annoying. If you use a class instead of a base type (such as int, string) to store the customer's data, the compiler will not allow that to happen (the CustomerID and ProductID are confused, the translator notes).

    • Do not trust external data

External data is unreliable, and our software programs must be rigorously checked before using them. Whether these external data comes from a registry, database, hard disk, socket, or a file you write with your keyboard, all of these external data must be checked strictly before use. Many times, I see some programs that fully trust configuration files, because programmers who develop these programs always think that no one will edit the configuration file and corrupt it.

    • Trusted devices: Camera, mouse, and keyboard

When you need to use external data, you may encounter the following situations:

1) Not enough security privileges

2) data does not exist

3) Incomplete data

4) The data is complete, but the format is not correct

This can happen regardless of whether the data source is a key in the registry, a file, a socket socket, a database, a Web service, or a serial port. All external data will always have the possibility of failure.

    • "Write operations" may also fail

Untrusted data sources are also an untrusted data warehouse. When you store data, similar situations can still occur:

1) Not enough security privileges

2) device does not exist

3) There is not enough space

4) A physical error occurred on the storage device

This is why some compression software creates a temporary file when it is working, and then renames it when the work is done, rather than modifying the source file directly. The reason is that if the hard disk is damaged (or the software is abnormal) it may cause the original data to be lost. (The translator has encountered this situation, backup data, power loss, the original old version of the backup was damaged, the translator note)

Secure programming

A friend of mine told me that a good programmer never writes bad code in his program. I think it's only necessary to be a good programmer, not a sufficient condition. Below I've collated some "bad code" that you might write when you do exception handling:

    • Do not throw "new Exception ()"

Please don't do that. Exception is a very abstract exception class, and catching such exceptions usually has a lot of negative effects. In general, we should define our own exception classes, and we need to differentiate between the exceptions thrown by the system (framework) and the exceptions we throw ourselves.

    • Do not store important exception information in the message property

Exceptions are encapsulated in the class. When you need to return the exception information, store the information in separate properties (not in the message property), otherwise it is difficult to resolve the information they need from the message property. For example, if you just need to correct the spelling mistakes, if you write the error message and other hints in string form in the message property, then how can others simply get the error message they want? It's hard for you to imagine how much effort they are going to make.

    • Each thread must contain a try/catch block

General exception handling is placed in the program in a relatively centralized place. Each thread needs to have a try/catch block, or you'll miss out on some of the exceptions to make it difficult to understand. When a program opens multiple threads to handle background tasks, you typically create a type to store the results of each thread's execution. Please don't forget to add a field to the type to store the exceptions that each thread might have, otherwise the main thread will not know the exception of the other threads. In some "instant forget" occasions (meaning that the main thread is no longer concerned about the running of threads, the translator notes), you may need to copy the exception handling logic from the main thread to your child thread.

    • To record after catching an exception

It doesn't matter how your program logs--log4net, EIF, Event log, tracelisteners, or text files, and so on. The important thing is that when you encounter an exception, it should be recorded in the log somewhere. But just record it once, otherwise you'll end up with a very large log file that contains a lot of duplicate information.

    • Do not log only the value of exception.message, you need to record exception.tostring ()

When we talk about logging, don't forget that we should record the value of exception.tostring () instead of exception.message. Because Exception.tostring () contains the stack trace information, the internal exception information, and the message. This information is usually very important, and if you only record exception.message, you may see a hint like "object reference does not point to instances in heap".

    • To catch a specific exception

If you want to catch an exception, catch the specific exception (not exception) as much as possible.

I often see beginners say that a good code is a code that cannot throw an exception. In fact, this is wrong, the good code should throw the corresponding exception when necessary, and the good code can only catch the exception that it knows how to handle (note this sentence, the translator note).

The following code is used as a description of this rule. I bet the guy who wrote the code below saw that it would kill me, but it really was a piece of code that was extracted from real programming.

The first class MyClass in one assembly, and the second class genericlibrary in another assembly. It works on a developed machine, but always throws "data illegal!" on the test machine. "Exception, although the data entered each time is valid.

Can you tell me why this is?

The reason for this problem is that exception handling is not very specific. As described on MSDN, the Convert.ToInt32 method only throws ArgumentException, FormatException, and OverflowException three exceptions. So, we should just deal with these three exceptions.

The problem occurred on the steps of our program installation, and we did not package the second assembly (GenericLibrary.dll) in. So after the program runs, the Converttoint method throws a FileNotFoundException exception, but the exception we catch is exception, so we'll prompt "data is not legal."

    • Do not abort the exception on the throw

In the worst case, you write code like catch (Exception) and do nothing in the catch block. Please do not do so.

    • The cleanup code is placed in the finally block

Most of the time, we handle only certain exceptions, and other exceptions are not handled. Then we should have more finally blocks in our code (even if an unhandled exception occurs, you can do something in the finally block, such as the code to clean up the resource, close the stream, or reply to the status). Please take this as a habit.

One thing that is easy to overlook is how to make our try/catch blocks both readable and robust. For example, suppose you need to read the data from a temporary file and return a string. No matter what happens, we have to delete this temporary file because it is temporary.

Let's take a look at the simplest code that doesn't use Try/catch blocks:

This code has a problem, the ReadToEnd method may throw an exception, then the temporary file can not be deleted. So some people change the code to:

This code becomes more complex, and it contains repetitive code.

So now let's look at a more robust way to use try/finally:

Where did the variable filecontents go? It is no longer needed because the return point is in front of the cleanup code. This is the benefit of having the code execute after the method returns: You can clean up the resources that are needed for the return statement (the resources that are needed to return the method), so the resource can only be released after the method returns, the translator notes.

    • Don't forget to use the using

Just calling the Dispose () method of an object is not enough. The Using keyword can prevent resource leaks even when an exception occurs. (for the use of the Dispose () method of an object, you can follow my book and have a chapter devoted to it.) Translator Note )

    • Do not use special return values to represent exceptions that occur in a method

Because there are many problems with this:

1) It is faster to throw the exception directly, because when we use a special return value to represent an exception, we need to check the return result each time we call the method, and this will occupy at least one register. Reduce code run speed.

2) Special return value can be, and is likely to be ignored

3) The special return value cannot contain stack trace information and cannot return the details of the exception

4) Many times, there is no special value to represent the exception that occurs in the method, such as when the divisor is zero:

    • Do not use the "throw exception" method to indicate that the resource does not exist

Microsoft recommends that, in some specific situations, methods can indicate that a method is not expected to occur during execution by returning some specific values. I know the rules I mentioned above are the opposite of this advice, and I don't like it. But some APIs do use some special return values to represent the exceptions in the method and work well, so I still think you can follow this advice with caution.

I saw a lot of the. NET Framework API methods that get resources use special return values, such as the Assembly.getmanifeststream method, which returns null (without throwing an exception) when the resource is not found (exception).

    • Do not use "throw exception" as a result of function execution

This is a very bad design. The code contains too many Try/catch blocks to make the code difficult to understand, the appropriate design can fully satisfy a method to return a variety of execution results (it is never possible to use the way to throw an exception to explain how the results of the method execution, the translator note), If you do need to throw an exception to represent the result of a method's execution, it only means that you have done too many things in this way and must be split. ( the original meaning of this is that unless there is an exception, a method should not simply throw an exception to illustrate the result of execution, that is, no disease groan, "the translator notes .")

    • You can use the "throw exception" method to highlight errors that cannot be ignored

I can give a real-world example. I developed a login API for my grivo (one of my products), and if the user fails to log in or the user does not call the login method, they will fail when they call the other method. I did this when I was designing the login method: If the user fails to log in, it throws an exception instead of simply returning false. Because of this, the caller (user) does not ignore the fact that (he is not logged in).

    • Do not empty the stack trace information

Stack trace information is the most important information when an exception occurs, we often need to handle some exceptions in the catch block, and sometimes we need to re-throw the exception (Re-throw). Here's a look at two methods (one of the correct errors):

The wrong approach:

Why is it wrong? Because when we examine the stack trace information, the source of the exception error becomes "THORW ex;", which hides the location where the real exception is thrown. Try this approach:

What's the change? We use "throw;" Instead of "throw ex;", the latter clears the original stack trace information. If we do not specify a specific exception (simple throw) when throwing an exception, it will default to the original caught exception. In this case, the exception that is caught by the upper code is the same exception that we caught with the catch at the very beginning.

    • The exception class should be marked as Serializable

Most of the time, our anomalies need to be able to be serialized. When we derive a new exception type, please do not forget to add the Serializable property to it. Who knows if our exception class will be used in remoting call or Web services?

    • Use "Throw exception" instead of Debug.Assert

When we publish the program, don't forget that Debug.Assert will be ignored. When we do some checking or validation work in the code, it is best to use the exception-throwing method instead of the output debug information.

Use this method of outputting debug information for unit tests or those that only need to be tested when the software is actually released to ensure that there are no errors.

    • Each exception class consists of at least three construction methods

It's pretty simple to do this (just paste the same code from other types), and if you don't, it's hard to follow some of the rules given above when someone else is using the type of exception you're writing.

What construction methods do I refer to? These three construction methods can be found here.

Don't reinvent the wheel.

There are a lot of frameworks or libraries that have done a better job of exception handling, with two Microsoft:

Exception Management Application Block

Microsoft Enterprise Instrumentation Framework

Note that if you don't follow some of the rules I've mentioned above, these libraries may be of little use to you.

vb.net

If you've read the full article, you'll see that all the sample code is written in C #. That's because C # is what I like more. NET language, and vb.net has some special rules of its own.

    • Simulating a using statement in C #

Unfortunately, VB. There is no using statement in net. You have to do this every time you release an unmanaged resource for an object:

If you do not call the Dispose method in the way described above, you are likely to get an error ( call for the Dispose method, follow the new book .) Translator note ).

    • Do not use unstructured exception handling (on Error Goto)

Unstructured exception handling is also called "on Error Goto", Djikstra (Eizhor Dextra) said in 1974, "Goto statement is harmful," this is already 30 ago! Please delete all the goto statements in your code, and I assure you that they have no benefit. ( Eizhor Dextra proposed the "Goto Harmful Theory", the semaphore and the PV primitive to solve the interesting problem of dining philosophers. He was mentioned in the book "Software Story", which speaks Fortran language. Translator Note )

Summarize

I hope this article will enable some people to improve their coding quality, and hopefully this article will discuss how to get started on how to handle exception handling effectively, and let us write programs that are more robust.

Translator's speech:

I have a shortcoming, do not know whether there are netizens like me. I am a slow-hot type of person, the same to the technology, a lot of things in the peak period of the past I began to feel. The main reason is that I am not too cold to fresh things, and I always feel that the original learning things have not mastered good, a bit of the meaning of halfway. In fact, I also know that this is very bad, after all, it industry is a rapid development of the industry, one did not keep up with the pace behind.

It is meeting such contradictory situations, I am learning knowledge is the focus of learning technology between the continuity, the so-called continuity, that is, ten years, 20 years or even 30 years is not very likely to change, not very likely to decline things, if you are engaged in the actual development of the company has been using a certain framework, if you are dead grasp " How to use this framework to make a good system "no, in a few years you may be outdated." And if you study the commonality in programming, such as protocol, interaction between systems, etc., these are used in every network communication system, whether it is a seemingly outdated PC program, or Web programs, or the current popular mobile apps, will be used, and the rationale is the same. See more, find new things come out as if the feeling (slightly exaggerated:-))

So I give advice to people who are just like me, who are less likely to follow something new, or those who have been working on a certain type of fixed development job for a long time: find the commonalities between technologies, and don't stay on the technical surface unless you're interested in something new and have enough energy.

These words are also shared by our company when we open a seminar.

. NET exception handling Best Practices (translated)

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.