Reputation: 541
From MSDN, code analysis warning CA1032:
Exception types must implement the following constructors:
- public NewException()
- public NewException(string)
- public NewException(string, Exception)
- protected or private NewException(SerializationInfo, StreamingContext)
I understand the purpose behind the serialization constructor, but what is the rationale behind "requiring" the others?
Why shouldn't I just define whatever constructors make sense for usage of my custom exception? What if I never want to throw MyException
without passing in a message– why should I define a parameterless constructor? What if I want MyException
to have an int
property and I only want constructors that initialize that property?
Upvotes: 49
Views: 10120
Reputation: 1340
It's just another Cargo cult. Someone once decided it's a good idea to make so (obviously most probably having strong relevant justifying experience) but with years of language evolution real reasoning has been replaced by repeating the dogma: just do as we used to do, don't ask.
Do we really want to allow custom exception being created with uninitialized custom property (if one presented)?
Do we really want to support serialization of our custom exception?
One aspect we most probably want is possibility to instantiate custom exception from client code to make it possible to test how our exception is handled.
Upvotes: 4
Reputation: 41
Why? Because. (Because from other answers, the parameterless constructor is required for exception routing.)
I'm in the same place, and I don't want to accidentally construct an exception without my error code parameter. So here's what I did:
public class MyException : ArgumentException
{
[Obsolete("Use a constructor that takes an ErrorCode instead")]
public MyException() { }
[Obsolete("Use a constructor that takes an ErrorCode instead")]
public MyException(string message) : base(message) { }
[Obsolete("Use a constructor that takes an ErrorCode instead")]
public MyException(string message, Exception innerException) : base(message, innerException) { }
public MyException(ErrorCode errorCode, string message) : base(message) { /* use error code */ }
public MyException(ErrorCode errorCode, string message, Exception innerException) : base(message, innerException) { /* use error code */ }
protected MyException(SerializationInfo info, StreamingContext context) : base(info, context)
{
if (info != null)
{
// error code stuff
}
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
if (info != null)
{
// error code stuff
}
}
}
Upvotes: 0
Reputation: 284796
This is a warning, not a requirement. It's basically principle of least surprise. Providing all 4 makes it easier for people used to "regular" C# exceptions to use yours. If you have a good reason to ignore the guideline, do so. But it will break certain usage scenarios, and make your class a little less intuitive.
Upvotes: 19
Reputation: 32960
Implementing the standard exception constructors allow people to use your exception in a standard, familiar way that is built into all existing .NET exceptions. The first three can be optional, if for some reason you don't want one of them to be used (although why you would want that I couldn't fathom.) However, the last one is the deserialization constructor, and if you wish your exception to be supported in any kind of distributed environment (.NET Remoting, ASP.NET Web Services, WCF, etc.), then its is pretty much essential.
Without a deserialization constructor and the [Serializable] attribute, your exceptions won't function in a distributed environment, and could possibly cause other problems. Given that, and the aspect of familiarity to well-versed C# developers, its best to implement at least the 4 standard exception constructors, and mark your exceptions with [Serializable].
Upvotes: 5
Reputation: 158309
You have gotten some good answers. I just want to add that providing these extra constructors does not necessarily require a lot of coding. Since they are already implemented in the base class, you can simply let that one do the work:
public class MyCustomException : Exception
{
public MyCustomException() : base() { }
public MyCustomException(string message) : base(message) { }
public MyCustomException(string message, Exception innerException) : base(message, innerException) { }
// and so on...
}
So you will only need to implement code where the behaviour of your exception deviates from that of the base class.
Upvotes: 12
Reputation: 116674
The parameterless and Serialization constructors are used by generic "exception routing" code that needs to move exceptions around between domains (e.g. across the internet between a service and a client).
The one that takes another Exception
is so that it is possible to chain all exceptions via the InnerException
property.
Finally, there's the one that takes a message string, which helps to make use of exceptions reasonably consistent.
Upvotes: 9
Reputation: 71935
Well, the constructor that takes an inner exception is pretty much necessary to make a custom exception properly usable. Without it, if someone caught your exception, they couldn't fire off a more descriptive or appropriate exception while preserving your original one (and the information it carries, like the stack trace.)
Upvotes: -2