다음을 통해 공유


Choosing the Right Type of Exception to Throw

My last post about the ApplicationException resulted in some questions along the lines of “so, if not ApplicationException, what should I throw instead?” The answer to this question is partially covered by my old post on Exception Throwing and partially by some additional guidelines for creating custom exceptions that did not make it to the original post, but are now included in the FDG book. For your convenience, I included both parts of the guidance below:

Choosing the Right Type of Exception to Throw

After you have decided when you need to throw exceptions, the next step is to pick the right type of exception to throw. This section provides those guidelines.

þ Consider throwing existing exceptions residing in the System namespaces instead of creating custom exception types.

See section 7.3 for detailed usage guidelines of the most common standard exception types.

þ Do create and throw custom exceptions if you have an error condition that can be programmatically handled in a different way than any other existing exception. Otherwise, throw one of the existing exceptions. See section 7.4 for details on creating custom exceptions.

ý Do not create and throw new exceptions just to have ‘your team's’ exception.

þ Do throw the most specific (the most derived) exception that makes sense.

For example, throw ArgumentNullException and not its base type ArgumentException if a null argument is passed.

Annotation (Jeffrey Richter):

Throwing System.Exception, the base class of all CLS-Compliant exceptions is always the wrong thing to do.

Annotation (Brent Rector):

As described in more detail later, catching System.Exception is nearly always the wrong thing to do as well.

Now that you have chosen the correct exception type, you can focus on ensuring that the error message your exception delivers says what you need it to say.

Designing Custom Exceptions

In some cases, it will not be possible to use existing exceptions. In those cases, you’ll need to define custom exceptions. The guidelines in this section provide help on doing that.

ý Avoid deep exception hierarchies.

þ Do derive exceptions from System.Exception or one of the other common base Exceptions.

þ Do end exception class names with the ‘Exception’ suffix.

þ Do make exceptions serializable. An exception must be serializable to work correctly across application domain and remoting boundaries.

þ Do provide (at least) these common constructors on all exceptions. Make sure the names and types of the parameters are exactly as in the example below.

public class SomeException: Exception, ISerializable {

   public SomeException();

  public SomeException(string message);

  public SomeException(string message, Exception inner);

 

  // this constructor is needed for serialization.

   protected SomeException(SerializationInfo info, StreamingContext context);

}

þ Do report security sensitive information through an override of ToString only after demanding an appropriate permission.

If the permission demand fails, return a string excluding the security sensitive information.

Annotation (Rico Mariani):

Do not store the results of ToString in any generally accessible data structure unless that data structure suitably secures the string from untrusted code. This advice applies to all strings but since exception strings frequently contain sensitive information (such a file paths) I reiterate the advice here.

þ Do store useful security sensitive information in private exception state. Ensure only trusted code can get the information.

þ Consider providing exception properties for programmatic access to extra information (besides the message string) relevant to the exception.

Comments

  • Anonymous
    July 05, 2006
    PingBack from http://summinktosay.net/darkindustries/?p=15

  • Anonymous
    July 05, 2006
    KC, thanks for replying.
    I still have a question. What is the purpose of "ApplicationException"?

    The .net fx documentation says, "If you are designing an application that needs to create its own exceptions, derive from the ApplicationException class"

  • Anonymous
    July 07, 2006
    A small bit on Exceptions. I can'temphasize enough: "As described in more detail later, catching System.Exception

  • Anonymous
    July 07, 2006
    Trackback from DotNetKicks.com

  • Anonymous
    July 12, 2006
    Now that you're aware of the design guidlines for exception throwing, and know which exceptions types...

  • Anonymous
    July 27, 2006
    I like the little red X's and green checkboxes.  They make the Do's and Don'ts stick out a lot better.  Any chance of getting those in the MSDN2 documentation?  :)

  • Anonymous
    July 31, 2006
    Kyralessa,
    I contacted the MSDN people and they said they will look into it.
    Thanks for the feedabck.

  • Anonymous
    August 17, 2006
    I believe there are issues with using custom exceptions that need more guidelines.

    When the exception thrown will be entirely consumed within the assembly that defines the custom exception then the guidelines above are sufficient (and then it really does not need to be marked as serializable unless explicitly serialized internally)

    When the custom exception may be thrown across process boundaries, and especially across machine boundaries, then there are other factors that should be considered. The problem is that the entity that receives the exception must have a copy of the assembly that defines the exception to be able to deserialize the exception. If it does not have a copy of the assembly, or if it has the wrong version of the assembly, then a different exception will be thrown by the runtime when it attempts and fails to deserialize the custom exception. This is worse then not using a custom exception because the client will get a misleading exception type to deal with, and any extra data associated with the custom exception will be lost.  The ability of an application to correctly interpret and respond to exceptions is of vital importance, and in this situation the abiity to deal meaningfully with custom exception type will be lost or degraded.

    If this can happen then the custom exception should be mapped at the boundary of the source to an exception defined in a system assembly so that it can be easily consumed. (I feel this is such an important issue that the runtime should provide an automatic mapping service at the receiver to remove this burden from the sender, but that's a different issue).

    In general, when custom exceptions are exposed to external clients across process and machine boundaries then consideration must be given to the deployment and versioning issues of the assembly in which the custom exception is defined, and if clients will have access to this assembly. If these issues cannot be adequately addressed then consider using standard exceptions instead.

  • Anonymous
    September 29, 2006
    Hi Krzysztof,As described in more detail later, catching System.Exception is nearly always the wrong thing to do as well.I agree with you in principle, but the .NET mechanism of not being able to declare exceptions thrown by methods does lead to a lot of headaches. For example, if you properly document your code, you can ensure (not 100% safe) that many people are aware of the possible exceptions that can raised by the code you are calling into. If you don't document your code, well, too bad, and the only other options are to look at the source code (if available) or use ILDasm or some other tool to figure out possible exceptions that can be thrown by the code you're calling into.I'm not getting into a checked/unchecked exception debate here, but if a method CAN but does [not] have to declare the exceptions that it raises, [and] you add a compiler switch to raise a warning if not declared, that would help methods self-document themselves.Thoughts?-krishFor example: // legal, but a special compiler flag could raise a warning public void M1(Boolean flag) { if (flag) throw new MyException1(); else throw new MyException2(); } // legal, but the special compiler flag could raise a warning for Exception2 public void M1(Boolean flag) throws Exception1 { if (flag) throw new MyException1(); else throw new MyException2(); } // legal, no warnings when using special compiler flag public void M1(Boolean flag) throws Exception1, Exception2 { if (flag) throw new MyException1(); else throw new MyException2(); }In theory, this is similar to Java exceptions that extend from java.lang.RuntimeException (an unchecked exception). Methods that raise the RuntimeException (or sub-classes thereof) can - but do not have to - declare exceptions in the method signature.

  • Anonymous
    October 06, 2006
    Krish, we are thinking about improving the current exceptions story. I agree that what we have today can be way better. We want to strike the right balance without being too rigid (checked exceptions) and too ad-hoc where as you note it's very difficult to write very robust code.

  • Anonymous
    November 24, 2006
    I also think that you need to alter this exception story slightly. Specificially this item: "Do not create and throw new exceptions just to have ‘your team's’ exception." If you imagine having a typical multitier application, which has a UI that is going to use data by calling on a method from a data access class. You might have several data access classes (one for XML, one for databases, etc)...the XML reading and writing data access class might have to deal with XmlExceptions, the database one a SqlException. But it shouldn't propagate these back to the UI - instead in my view, your data acess team should create an exception class "the data access team's exception" - eg DataAccessException that is used for all of these classes to report back to the UI that an exception occured. Of course, this exception should set the InnerException correctly so that developers can drill down and see what they did wrong...if you don't do that, then you end up with an "abstraction leak" where the UI has to be aware of what the specific data access technology is being used at that time...

  • Anonymous
    November 26, 2006
    The comment has been removed

  • Anonymous
    December 04, 2006
    Nigel, I think the way to address the abstraction leak is to describe exceptions that can be handled in the contract of the abstraction. Having DataAccessTeamException does not help much: it's not an exception that can be handled, at least not more than System.Exception is. John, yes, there are cases where we don't have a good general "it's a bug" exception in the framework, but such cases are extremly rare. In such cases, I would add a new exception, but I would not name it after the team; I would name it so it describes how it's different from existing exceptions in the framework (i.e. describe why you could not throw any of the existing ones). But as I said, it's extremly rare. For example, InvalidOperationException might be a good candidate in cases you described.

  • Anonymous
    February 12, 2007
    為什麼推薦 ? Krzysztof Cwalina 是 .NET Framework API Design Team 的 Program Manager, 他也寫了 一本 Framework Design

  • Anonymous
    May 06, 2007
    Hi, In your framework design book you mention that one should avoid throwing COMException since it is for the CLR to throw it. We are in the process of writing a data access layer, which is in C#. However the first client of this layer is a COM/C++ application. In order to propagate exceptions from .Net to COM (so that the COM application gets a HResult) we are using COMException. Is this a resonable violation of the guideline, or is there a better way to communicate exception across interop boundaries? Thanks -Sundar

  • Anonymous
    October 09, 2007
    Jakoś ostatnio często wpadają mi do rąk różne teksty o wyjątkach w .Net. Z tego co da się zauważyć, to

  • Anonymous
    February 19, 2009
    You’ve seen the advice before— it’s not a good programming practice to catch System.Exception . Because