如果您创建一个自定义异常,并使用如下内容覆盖虚拟属性 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 实现 ISerialized
的事实有关,但由于 Message
属性是虚拟的,我是否应该不允许像这样覆盖它并仍然让它工作?
是否有正确的方法来实现 Exception 并能够覆盖 Message
属性并使其仍然有效?
最佳答案
发生这种情况是正确的,因为 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
中。
因此,如果您希望消息属性的值显示在 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
字段的值不再是基类所需的值GrossException
。但至少 JSON 很漂亮。
更好的解决方案是修改 GrossException
类型,使其具有 protected 构造函数,可以在其中指定消息:
public class GrossException : Exception
{
public GrossException() : base("Eww, gross") { }
protected GrossException(SerializationInfo info, StreamingContext context) : base(info, context) { }
protected GrossException(string message) : base(message) { }
}
或者,如果您只是想查看 JSON 中被覆盖的消息以进行调试(例如,因为您正在记录异常),您可以将其添加到序列化流中,如下所示:
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)
{
base.GetObjectData(info, context);
info.AddValue("OverriddenMessage", Message);
}
}
这两种解决方案都可以避免代码异味。
关于c# - 使用 JSON.NET 自定义异常覆盖消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39984110/