views:

135

answers:

4

The answer to What is the correct way to make exceptions serializable? says that the "correct" base implementation for a custom exception includes 4 ctors:

[Serializable]
public class SerializableExceptionWithoutCustomProperties : Exception
{
    public SerializableExceptionWithoutCustomProperties()
    {
    }

    public SerializableExceptionWithoutCustomProperties(string message) 
        : base(message)
    {
    }

    public SerializableExceptionWithoutCustomProperties(string message, Exception innerException) 
        : base(message, innerException)
    {
    }

    // Without this constructor, deserialization will fail
    protected SerializableExceptionWithoutCustomProperties(SerializationInfo info, StreamingContext context) 
        : base(info, context)
    {
    }
}

Right off the bat, I'd say that's a really bad name for an Exception type. But, beyond that,

  1. For purposes of binary serialization, which is what the SO question was referring to, must I implement all 4 constructors? I think for purposes of [Serializable], I must provide a ctor that accepts 2 args of type (SerializationInfo, StreamingContext), because the exception derives from System.Exception, which itself does custom serialization. I can understand that. But must I implement the other ctors, in order to properly provide a serializable exception? I know that if I want to allow a type to be xml-serializable, I need to provide the default (no-op) ctor. Is the same true for [Serializable]? For a moment, let's confine ourselves to the narrow concern of [Serializable], and leave aside any broader guidelines regarding "framework design".

  2. Moving to the broader question: The guidelines say that custom exceptions should implement the 4 common ctors. What is the reasoning behind this guideline? If I design a custom exception, is it really bad manners, or even a bug, if I don't provide a null/default ctor? Is it really bad manners, or even a bug, if I don't provide a ctor that allows an innerException? Why? Consider the case that my custom exception is generated within my library code, and the only instances I ever throw include a message, and no innerException.

  3. In short, is the following code acceptable for a custom exception that provides no additional properties?


[Serializable]
public class CustomException : Exception
{
    public CustomException(string message) : base(message) { }

    // Without this constructor, deserialization will fail
    protected CustomException(SerializationInfo info, StreamingContext context) 
        : base(info, context) { }
}

see also: Winterdom blog: Make exceptions Serializable.

A: 

Suggestion: if others will be using your exception, and since these others will be familiar with .NET exceptions, then why not just follow the guidelines? They are the same that are used by the .NET Framework.

Are you feeling this is too much work, given the number of custom exception types you're defining? If so, then this would be another reason to follow the guidelines, which specify that you should not create a large number of custom exceptions.

John Saunders
No, not too much work - the constructors are already in my code. But, my code doesn't use half of them, and I wanted to consider removing them as part of code cleanup. These exceptions are not intended for use outside the library I'm providing. They are thrown by the library but I would not expect any other code to throw them. Is it typical that I should allow that, support that?
Cheeso
If they're all for your library, make them internal classes. You should still implement all these constructors and such - you or later maintainers will expect them to be "just like every other exception in .NET".
John Saunders
+1  A: 

The relevant Code Analysis warning is CA1032, and the reference page provides this justification:

Failure to provide the full set of constructors can make it difficult to correctly handle exceptions. For example, the constructor with the signature NewException(string, Exception) is used to create exceptions that are caused by other exceptions. Without this constructor, you cannot create and throw an instance of your custom exception that contains an inner (nested) exception, which is what managed code should do in such a situation. The first three exception constructors are public by convention. The fourth constructor is protected in unsealed classes, and private in sealed classes.

Only you or your project team can decide if your situation warrants an exception (sorry...)

AakashM
ah, very clever. Thanks for the excerpt. That's helpful.
Cheeso
+1  A: 

Hey, I know that it's at least advised by Microsoft. If you use VS2008 (or maybe earlier versions) you can easily let Visual Studio create them for you by typing

exception

in the editor and pressing Tab (twice?). This will create them, giving you the chance to give the class a new name.

Lennaert
A: 

I didn't like any of the answers. I'm settling on my proposed solution, which is .. that I can eliminate the "normal" constructors on my custom exception, including the default constructor and the nested constructor. Also, I need to make sure serialization works, because of cross-appdomain calls.

Cheeso