Pokud například v nějaké knihovně implementujeme veřejné třídy pro vlastní výjimky, měli by tyto třídy být implementované jako serializovatelné objekty. Obecně totiž nemusíme vědět, zda se nevyskytne případ, kdy exception objekt bude muset překročit hranici aplikační domény a pak by chybná implementace vadila.
Zajistit korektnost implementace tříd odvozených z Exception není přitom vůbec nic složitého, ale zase na druhou stranu to není úplně automatické a je tedy přece jenom potřeba něco málo dodržet.
Konkrétně jsou to tyto náležitosti:
- Označit třídu atributem Serializable.
- Doplnit protected nebo private (v případě sealed třídy) konstruktor s parametry SerializationInfo a StreamingContext. Tento konstruktor provádí volání konstruktoru base(info, context) základní třídy a případnou deserializaci členských dat.
- V případě vlastních členských dat doplnit implementaci metody GetObjectData pro serializaci těchto dat.
Všechny tyto body vychází z toho, že třída Exception pro serializaci implementuje interface ISerializable.
Implementace výjimky, která neobsahuje žádná vlastní členská data bude například následující:
[Serializable]
public class InvalidImportFileException : Exception, ISerializable
{
#region constructors and destructors
public InvalidImportFileException(string message) : base(message) { }
public InvalidImportFileException(string message, Exception innerException) : base(message, innerException) { }
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
protected InvalidImportFileException(SerializationInfo info, StreamingContext context) : base(info, context) { }
#endregion
}
Explicitní uvedení, že naše třída implementuje interface ISerializable není nutná (již tak činí základní třída Exception), zde ho uvádím jen pro názornost.
Pokud by byla implementovaná třída sealed, byl by konstruktor s parametry SerializationInfo a StreamingContext pouze private (tento konstruktor je při deserializaci objektu volán pomoci reflexe, takže to, že je private nevadí).
Implementace výjimky, která obsahuje vlastní členská data – v příkladu se jedná o vlastnost ProviderName -bude následující:
[Serializable]
public class ProviderException : Exception, ISerializable
{
#region member varible and default property initialization
public string ProviderName { get; private set; }
#endregion
#region constructors and destructors
public ProviderException(string providerName, string message)
: base(message)
{
if (providerName == null)
{
throw new ArgumentNullException("providerName");
}
this.ProviderName = providerName;
}
public ProviderException(string providerName, string message, Exception innerException)
: base(message, innerException)
{
if (providerName == null)
{
throw new ArgumentNullException("providerName");
}
this.ProviderName = providerName;
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
protected ProviderException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
if (info == null)
{
throw new ArgumentNullException("info");
}
this.ProviderName = (string)info.GetValue("ProviderName", typeof(string));
}
#endregion
#region action methods
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException("info");
}
info.AddValue("ProviderName", this.ProviderName);
base.GetObjectData(info, context);
}
#endregion
}
Zde musíme navíc doplnit vlastní implementaci serializace a deserializace členských dat.
V konstruktoru ProviderException(SerializationInfo info, StreamingContext context) je logika pro jejich deserializaci a v metodě GetObjectData pro serializaci. Metoda GetObjectData musí dále provádět volání base implementace.