序列化/反序列化和非默认构造函数

时间:2012-11-26 09:51:09

标签: c# serialization reflection attributes initialization

考虑这个课程:

[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可能是因为这没有意义。

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

2 个答案:

答案 0 :(得分:4)

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

例如,在BinaryFormatter中,SoapFormatterDataContractSerializer使用以下技术对对象进行反序列化:

  1. 致电FormatterServices.GetUnitializedObject
  2. 获取“原始”对象
  3. 在构造对象上调用单独的预序列化方法(通过检查标有OnSerializingAttribute的方法)。
  4. 反序列化对象的状态(通过检查相应的属性来了解序列化程序应该跳过哪些字段以及它应该反序列化的字段)。
  5. 对反序列化对象调用后序列化方法(通过检查标有OnSerializedAttribute的方法)。
  6. 另一个XmlSerializer使用绝对不同的算法:它需要无参数构造函数,应该用作“预序列化”和“序列化后”步骤。

    所以我的观点是它完全取决于序列化程序的类型及其实现。而且仍然需要序列化程序的作者和序列化程序的消费者的一些心理努力。

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

答案 1 :(得分:1)

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

[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
}

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