Часто приходится создавать свои исключения с параметром. Операция однотипная, делается по шаблону. По этому хотелось бы иметь максимально приближенный к стандартам шаблон с учетом всех нюансов.
[Serializable]
// Important: This attribute is NOT inherited from Exception, and MUST be specified
// otherwise serialization will fail with a SerializationException stating that
// "Type X in Assembly Y is not marked as serializable."public class SerializableExceptionWithCustomProperties : Exception
{
private readonly string resourceName;
private readonly IList<string> validationErrors;
public SerializableExceptionWithCustomProperties()
{
}
public SerializableExceptionWithCustomProperties(string message)
: base(message)
{
}
public SerializableExceptionWithCustomProperties(string message, Exception innerException)
: base(message, innerException)
{
}
public SerializableExceptionWithCustomProperties(string message, string resourceName, IList<string> validationErrors)
: base(message)
{
this.resourceName = resourceName;
this.validationErrors = validationErrors;
}
public SerializableExceptionWithCustomProperties(string message, string resourceName, IList<string> validationErrors, Exception innerException)
: base(message, innerException)
{
this.resourceName = resourceName;
this.validationErrors = validationErrors;
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
// Constructor should be protected for unsealed classes, private for sealed classes.
// (The Serializer invokes this constructor through reflection, so it can be private)protected SerializableExceptionWithCustomProperties(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 statebase.GetObjectData(info, context);
}
}
Еще рекомендуют добавлять атрибут ComVisible(true) для класса.
И не ясно зачем [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]? Насколько это правильно? Посмотрел базовые классы .Net -- в ArgumentException стоит атрибут SecurityCritical на GetObjectData, но никакого на конструкторе SerializationInfo info, StreamingContext context.
Хотелось бы иметь правильный на 100% шаблон. Какие атрибуты нужно проставлять, как в примере или как в системной библиотеке?
Re: Канонический шаблон создания Custom Exception с параметром
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, hardcase, Вы писали:
H>>С другой стороны, какой смысл в том, чтобы делать все исключения сериализуемыми?
+1!
А>Если есть вероятность, что вашу библиотеку будут вызывать удаленно через WCF -- то нужно делать все.
-1! По умолчанию, исключения не могут покидать границы сервиса вообще, но даже если вы захотите это сделать, то стандартные исключения через границу WCF все равно не пролезут.
Тут куча проблем: WCF — это SOA, а значит исключения метода являются частью сигнатуры и должны четко специфицироваться с помощью FaultContract-а. Во-вторых, исключения по своей природе полиморфные и могут содержать члены любых типов (objects, IList, etc), а по умаолчанию стандартный сериализатор (DataContractSerializer) просто их не пережует и рухнет с ошибкой. Подробнее см. в статье на кывте — Известные типы (Known Types) в WCF
Вот границы домена — это действительно более разумный аргумент. Но и тут нужно разделять "потенциальную возможность" (все возможно) от "практической возможности" (да, возможно, но какого х вы должны разрезать домены приложения кастомными исключениями?)
А>Если не ошибаюсь, то по стандарту все кастомные исключения должны быть сериализуемыми.
По этому же стандарту (речь же про Framework Design Guidelines, верно) нужно просто Avoid создавать свои собственные исключения и делать это лишь тогда, когда вы понимаете, как оно поможет в определенных сценариях восстановления.
В общем, я лично не делаю исключения сериализируемыми, поскольку это захламляет код, а пользы от этого практически никакой, ибо необходимость в их сериализации — это corner case, который я покрою лишь в случае четкой необходимости.