更具体地说,当异常包含自定义对象时,这些对象本身可能是可序列化的,也可能不是。

举个例子:

public class MyException : Exception
{
    private readonly string resourceName;
    private readonly IList<string> validationErrors;

    public MyException(string resourceName, IList<string> validationErrors)
    {
        this.resourceName = resourceName;
        this.validationErrors = validationErrors;
    }

    public string ResourceName
    {
        get { return this.resourceName; }
    }

    public IList<string> ValidationErrors
    {
        get { return this.validationErrors; }
    }
}

如果这个异常被序列化和反序列化,两个自定义属性(ResourceName和ValidationErrors)将不会被保留。属性将返回null。

是否有通用的代码模式来实现自定义异常的序列化?


当前回答

异常已经是可序列化的,但是您需要重写GetObjectData方法来存储变量,并提供一个构造函数,可以在重新水化对象时调用该构造函数。

所以你的例子就是:

[Serializable]
public class MyException : Exception
{
    private readonly string resourceName;
    private readonly IList<string> validationErrors;

    public MyException(string resourceName, IList<string> validationErrors)
    {
        this.resourceName = resourceName;
        this.validationErrors = validationErrors;
    }

    public string ResourceName
    {
        get { return this.resourceName; }
    }

    public IList<string> ValidationErrors
    {
        get { return this.validationErrors; }
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter=true)]
    protected MyException(SerializationInfo info, StreamingContext context) : base (info, context)
    {
        this.resourceName = info.GetString("MyException.ResourceName");
        this.validationErrors = info.GetValue("MyException.ValidationErrors", typeof(IList<string>));
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter=true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);

        info.AddValue("MyException.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("MyException.ValidationErrors", this.ValidationErrors, typeof(IList<string>));
    }

}

其他回答

在. net Core中,. net 5.0及以上版本不使用Serializable,因为微软遵循在BinaryFormatter中发现的安全威胁实践。

使用存储在数据集合中的示例

异常已经是可序列化的,但是您需要重写GetObjectData方法来存储变量,并提供一个构造函数,可以在重新水化对象时调用该构造函数。

所以你的例子就是:

[Serializable]
public class MyException : Exception
{
    private readonly string resourceName;
    private readonly IList<string> validationErrors;

    public MyException(string resourceName, IList<string> validationErrors)
    {
        this.resourceName = resourceName;
        this.validationErrors = validationErrors;
    }

    public string ResourceName
    {
        get { return this.resourceName; }
    }

    public IList<string> ValidationErrors
    {
        get { return this.validationErrors; }
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter=true)]
    protected MyException(SerializationInfo info, StreamingContext context) : base (info, context)
    {
        this.resourceName = info.GetString("MyException.ResourceName");
        this.validationErrors = info.GetValue("MyException.ValidationErrors", typeof(IList<string>));
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter=true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);

        info.AddValue("MyException.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("MyException.ValidationErrors", this.ValidationErrors, typeof(IList<string>));
    }

}

我不得不认为,想要序列化一个异常强烈地表明您对某些事情采取了错误的方法。这里的终极目标是什么?如果在两个进程之间传递异常,或者在同一个进程的不同运行之间传递异常,那么异常的大多数属性在另一个进程中是无效的。

在catch()语句中提取所需的状态信息并将其存档可能更有意义。

实现ISerializable,并遵循执行此操作的常规模式。

您需要用[Serializable]属性标记类,并添加对该接口的支持,还需要添加隐含的构造函数(在该页上有描述,搜索暗示构造函数)。您可以在文本下面的代码中看到它的实现示例。

用[Serializable]标记类,尽管我不确定序列化器将如何处理IList成员。

EDIT

下面的帖子是正确的,因为你的自定义异常有接受参数的构造函数,你必须实现ISerializable。

如果使用默认构造函数,并使用getter/setter属性公开两个自定义成员,则只需设置属性即可。