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

举个例子:

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。

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


当前回答

为了补充上面的正确答案,我发现如果我将自定义属性存储在Exception类的Data集合中,就可以避免这种自定义序列化的事情。

例如:

[Serializable]
public class JsonReadException : Exception
{
    // ...

    public string JsonFilePath
    {
        get { return Data[@"_jsonFilePath"] as string; }
        private set { Data[@"_jsonFilePath"] = value; }
    }

    public string Json
    {
        get { return Data[@"_json"] as string; }
        private set { Data[@"_json"] = value; }
    }

    // ...
}

就性能而言,这可能比Daniel提供的解决方案效率更低,可能只适用于“整型”类型,如字符串和整数等。

尽管如此,这对我来说还是很容易理解的。

其他回答

异常已经是可序列化的,但是您需要重写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()语句中提取所需的状态信息并将其存档可能更有意义。

曾经有一篇来自Eric Gunnerson在MSDN上的优秀文章“脾气暴躁的例外”,但它似乎已经被撤下了。URL是:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp08162001.asp

Aydsman的答案是正确的,更多信息在这里:

http://msdn.microsoft.com/en-us/library/ms229064.aspx

我想不出带有不可序列化成员的Exception的任何用例,但如果您避免尝试在GetObjectData和反序列化构造函数中序列化/反序列化它们,应该是可以的。还要用[NonSerialized]属性标记它们,这更像是文档,因为您自己实现了序列化。

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

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

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

EDIT

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

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