如果您创建了一个自定义异常,它会覆盖虚拟属性Message
,如下所示:
public class GrossException : Exception
{
public GrossException() : base("Eww, gross") { }
}
public class BarfException : GrossException
{
public override string Message { get; } = "BARF!!";
}
然后,当通过ASP.NET使用JSON.NET发送BarfException
时,Message
属性将包含
“Eww,gross”
而不是被覆盖的值。我相信这与Exception实现ISerializable
的事实有关,但由于Message
属性是虚拟的,我是否应该允许这样覆盖它并仍然有效?
是否有正确的方法来实现Exception并且能够覆盖Message
属性并仍然可以使用它?
答案 0 :(得分:4)
这是正确的,因为Exception
实现了ISerializable
和Json.NET supports this interface。具体来说,在参考源中,在方法Exception.GetObjectData(SerializationInfo info, StreamingContext context)
中,Exception
类型序列化基础字段,而不是属性:
info.AddValue("Message", _message, typeof(String));
Microsoft可能已经这样做了,因为如果未设置基础字段,Message
属性具有默认值:
public virtual String Message {
get {
if (_message == null) {
if (_className==null) {
_className = GetClassName();
}
return Environment.GetResourceString("Exception_WasThrown", _className);
} else {
return _message;
}
}
}
通过序列化字段而不是属性,默认消息的异常将在反序列化时自动显示在接收系统的CurrentUICulture
中的可见消息。
因此,如果您希望message属性的值显示在JSON中,而不是基础字段,则需要覆盖GetObjectData()
。并且,如果您尝试添加与预先存在的值同名的值,AddValue()
会引发异常,并且SerializationInfo
没有SetValue()
方法来替换当前值,那么我需要做一些代码味道的事情:
public class GrossException : Exception
{
public GrossException() : base("Eww, gross") { }
protected GrossException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
public class BarfException : GrossException
{
public BarfException() : base() { }
protected BarfException(SerializationInfo info, StreamingContext context) : base(info, context) { }
public override string Message { get { return "BARF!!"; } }
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
var tempInfo = new SerializationInfo(GetType(), new FormatterConverter());
base.GetObjectData(tempInfo, context);
foreach (SerializationEntry entry in tempInfo)
{
if (entry.Name != "Message")
{
info.AddValue(entry.Name, entry.Value, entry.ObjectType);
}
}
info.AddValue("Message", Message);
}
}
正如您所看到的,此解决方案违反了继承层次结构的设计,因为基础_message
字段的值不再是基类{{1}所需的值 }。但至少JSON很漂亮。
更好的解决方案是修改GrossException
类型以使其具有可以指定消息的受保护构造函数:
GrossException
或者,如果您只是想在JSON中查看被覆盖的消息以进行调试(例如,因为您正在记录异常),您可以将其添加到序列化流中,如下所示:
public class GrossException : Exception
{
public GrossException() : base("Eww, gross") { }
protected GrossException(SerializationInfo info, StreamingContext context) : base(info, context) { }
protected GrossException(string message) : base(message) { }
}
任何一种解决方案都可以避免代码异味。