【问题标题】:Serialization/Deserialization and non-default constructors序列化/反序列化和非默认构造函数
【发布时间】:2012-11-26 09:51:08
【问题描述】:

考虑这个类:

[Persistable]
public sealed class FileMoveTask : TaskBase
{
     [PersistMember]
     public string SourceFilePath { get; private set;}

     [PersistMember]
     public string DestFilePath { get; private set;}

     public FileMoveTask(string srcpath, string dstpath)
     {
         this.SourceFilePath = srcpath;
         this.DestFilePath = dstpath;

         //possibly other IMPORTANT initializations
     }

     //code
}

我可以通过序列化具有属性PersistMember 的所有成员来持久化此类的对象。但是我在反序列化过程中遇到了一些问题(设计问题)。特别是,问题在于构造函数中可能存在的“可能还有其他重要的初始化”,程序员可能会决定让少数成员可持久化(即不向他们添加PersistMember)可能是因为这没有意义。

在这种情况下,我如何将对象反序列化到与它完全相同的状态?我想,这个问题归结为:我将如何调用非默认构造函数,将之前传递的 same 参数传递给它?有没有办法做到这一点?我们可以制定一些可以由编译器强制执行的规则(某种元编程)吗?构造函数属性在这里可以提供帮助吗?

【问题讨论】:

  • 您使用的是什么类型的序列化? XML,...?
  • @RuiJarimba:我正在使用我自己编写的自定义序列化。将对象序列化为 XElement 类型的 XML 对象。

标签: c# serialization reflection attributes initialization


【解决方案1】:

解决此问题的最简单方法是使用现有的众所周知的技术。 例如,看看 .NET 框架中使用的其他序列化机制(您会注意到其中的巨大差异)。

例如,BinaryFormatterSoapFormatterDataContractSerializer 使用以下技术来反序列化对象:

  1. 通过调用FormatterServices.GetUnitializedObject获取“原始”对象
  2. 在构造对象上调用单独的预序列化方法(通过检查标有OnSerializingAttribute 的方法)。
  3. 反序列化对象的状态(通过检查适当的属性来了解序列化程序应该跳过哪些字段以及应该反序列化哪些字段)。
  4. 在反序列化对象上调用序列化后方法(通过检查标有OnSerializedAttribute 的方法)。

另一方面,XmlSerializer 使用完全不同的算法:它需要无参数构造函数,应该用作“预序列化”和“后序列化”步骤。

所以我的意思是它完全取决于序列化程序的类型及其实现。并且仍然需要序列化程序的作者和序列化程序的消费者都付出一些脑力劳动。

所以我强烈建议使用现有技术之一,但不要发明轮子(比如添加一些其他自定义属性来恢复对象的状态)。您甚至可以使用现有属性来简化从 .NET 序列化工具到自定义序列化机制的迁移(以及使用附加属性,如 NonSerializableAttrubite)。

【讨论】:

  • 同意,无需重新发明轮子
【解决方案2】:

您可以通过两种方式解决这个问题。使用约定优于配置(将构造函数参数命名为属性,并且不包含默认构造函数):

[Persistable]
public sealed class FileMoveTask : TaskBase
{
     [PersistMember]
     public string SourceFilePath { get; private set;}

     [PersistMember]
     public string DestFilePath { get; private set;}

     public FileMoveTask(string sourceFilePath, string destFilePath)
     {
         this.SourceFilePath = srcpath;
         this.DestFilePath = dstpath;

         //possibly other IMPORTANT initializations
     }

     //code
}

使用您的属性显式标记构造函数参数并搜索已标记的构造函数:

[Persistable]
public sealed class FileMoveTask : TaskBase
{
     [PersistMember]
     public string SourceFilePath { get; private set;}

     [PersistMember]
     public string DestFilePath { get; private set;}

     public FileMoveTask([PersistMember("SourceFilePath")]string srcpath, [PersistMember("DestFilePath")]string dstpath)
     {
         this.SourceFilePath = srcpath;
         this.DestFilePath = dstpath;

         //possibly other IMPORTANT initializations
     }

     //code
}

属性或参数名称不是用来知道要设置哪个属性,而是要知道调用构造函数时要使用序列化数据中的哪些信息。

【讨论】:

  • 这似乎已经足够了。我会试试这个。 +1 并被接受。
猜你喜欢
  • 1970-01-01
  • 2018-08-18
  • 1970-01-01
  • 1970-01-01
  • 2014-09-07
  • 1970-01-01
  • 2016-07-07
  • 1970-01-01
  • 2018-05-15
相关资源
最近更新 更多