Daniel Fortunov's Adventures in Software Development » Serializable Exceptions with custom properties
0 Comments- Add comment |
Back to Software Development Blog Written on 10-Feb-2009 by asquiIf you define your own .NET Exception type (derived from System.Exception) it is important to remember that this custom exception type will not be serializable by default.
It is often necessary for Exceptions be serializable because without this they will not travel across remoting boundaries, or even between application domains within the same process.
It is the [Serializable] attribute that indicates if your type is serializable. Although System.Exception is decorated with the [Serializable] attribute, this attribute will not be inherited by your custom exception class. The reason for this is clear when we open up Reflector and look at the declaration of SerializableAttribute:
[ComVisible(true)]
[AttributeUsage(
AttributeTargets.Delegate | AttributeTargets.Enum |
AttributeTargets.Struct | AttributeTargets.Class,
Inherited=false)] // Not inherited!
public sealed class SerializableAttribute : Attribute
{ ... }
This means that any custom exception types you create must be explicitly decorated with the [Serializable] attribute.
The default serialization behaviour (which you get for free when you just decorate your class with [Serializable]) is to serialize all public and private fields in the type. This is good enough for many cases, but System.Exception needs to do some special things when being serialized and de-serialized. (For example, populate the lazily instantiated stack trace, which may not have been generated if the default serializer tries to just reach in and grab the field values through reflection.)
Because System.Exception implements the ISerializable interface, by inheritance, your derived exception class also necessarily implements this interface. This means you cannot rely on any default serialization behaviour.
You have to play along with the full ISerializable pattern. This involves two things:
Here is an example custom exception which defines additional properties and serializes them correctly.
[Serializable]
// Attribute is NOT inherited from Exception and MUST be specified.
public class SerializableException : Exception
{
private readonly string resourceName;
private readonly IList<string> validationErrors;
public SerializableException()
{
}
public SerializableException(string message)
: base(message)
{
}
public SerializableException(string message, Exception inner)
: base(message, inner)
{
}
public SerializableException(string message, string resourceName, IList<string> validationErrors)
: base(message)
{
this.resourceName = resourceName;
this.validationErrors = validationErrors;
}
public SerializableException(string message, string resourceName, IList<string> validationErrors, Exception inner)
: base(message, inner)
{
this.resourceName = resourceName;
this.validationErrors = validationErrors;
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
// Protected for unsealed classes, private for sealed.
protected SerializableException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
this.resourceName = info.GetString("ResourceName");
this.validationErrors = (IList<string>)info.GetValue("ValidationErrors", typeof(IList<string>));
}
public string ResourceName
{
get { return this.resourceName; }
}
public IList<string> ValidationErrors
{
get { return this.validationErrors; }
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException("info");
}
info.AddValue("ResourceName", this.ResourceName);
// Note: if "List<T>" isn't serializable you may need to work out another
// method of adding your list, this is just for show...
info.AddValue("ValidationErrors", this.ValidationErrors, typeof(IList<string>));
// MUST call through to the base class to let it save its own state
base.GetObjectData(info, context);
}
}
You can also download a VS2008 project with a more complete set of examples, including unit tests.
(This post was based on my Stack Overflow question, “What is the correct way to make a custom .NET Exception serializable?”)