更具体地说,当异常包含自定义对象时,这些对象本身可能是或可能不是可序列化的。
举个例子:
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; }
}
}
如果此Exception被序列化和反序列化,则两个自定义属性(ResourceName
和ValidationErrors
)将不会保留。属性将返回null
。
自定义异常序列化的实现有没有通用的Code Pattern?
8条答案
按热度按时间axzmvihb1#
基本实现,无自定义属性
SerializableExceptionWithoutCustomProperties.cs:
完整实现,自定义属性
自定义可序列化异常(
MySerializableException
)和派生的sealed
异常(MyDerivedSerializableException
)的完整实现。关于此实现的要点总结如下:
1.您必须使用
[Serializable]
属性修饰每个派生类-此属性不是从基类继承的,如果未指定,序列化将失败,并显示SerializationException
,声明 “程序集Y中的类型X未标记为可序列化"1.必须实现自定义序列化。
[Serializable]
属性本身是不够的-Exception
实现了ISerializable
,这意味着派生类还必须实现自定义序列化。这涉及两个步骤:1.提供序列化构造函数。如果你的类是
sealed
,这个构造函数应该是private
,否则它应该是protected
,以允许访问派生类。1.重写GetObjectData(),并确保在最后调用
base.GetObjectData(info, context)
,以便让基类保存自己的状态。SerializableExceptionWithCustomProperties.cs:
DerivedSerializableExceptionWithAdditionalCustomProperties.cs:
单元测试
上面定义的三种异常类型的MSTest单元测试。
UnitTests.cs:
r8uurelv2#
Exception已经是可序列化的了,但是你需要重写
GetObjectData
方法来存储你的变量,并提供一个构造函数,当重新水合你的对象时可以调用它。所以你的例子变成了:
oxalkeyp3#
为了补充上面的正确答案,我发现如果将自定义属性存储在
Exception
类的Data
collection中,就可以避免这种自定义序列化操作。例如:
可能这在性能方面不如the solution provided by Daniel有效,并且可能只适用于“整数”类型,如字符串和整数等。
不过,这对我来说很容易,也很容易理解。
7gyucuyw4#
实现ISerializable,并按照normal pattern执行此操作。
您需要用[Serializable]属性标记类,并添加对该接口的支持,还需要添加隐含的构造函数(在该页面上进行了描述,搜索 * 意味着一个构造函数 *)。您可以在文本下方的代码中看到其实现的示例。
k97glaaz5#
曾经在MSDN上有一篇来自Eric Gunnerson的优秀文章“The well-tempered exception”,但它似乎已经被删除了。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]属性来标记它们,这更像是一个文档,而不是其他任何东西,因为您是自己实现序列化的。
xqnpmsa86#
在.NET Core中,.Net 5.0及更高版本不使用Serializable,因为Microsoft遵循BinaryFormatter中的安全威胁实践。
使用存储在数据收集中的示例
siotufzp7#
用[Serializable]标记这个类,尽管我不确定序列化器处理IList成员的效果如何。
编辑
下面的帖子是正确的,因为您的自定义异常具有接受参数的构造函数,所以您必须实现ISerializable。
如果使用默认构造函数并使用getter/setter属性公开两个自定义成员,则只需设置属性即可。
i7uq4tfw8#
我不得不认为,想要序列化一个异常是一个强烈的迹象,表明你正在采取错误的方法。最终目标是什么?如果在两个进程之间传递异常,或者在同一进程的不同运行之间传递异常,那么异常的大多数属性在另一个进程中都是无效的。
在catch()语句中提取所需的状态信息并将其存档可能更有意义。