1

考虑这个类:

[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到它们),因为这没有意义。

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

4

2 回答 2

4

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

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

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

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

所以我的观点是它完全取决于序列化程序的类型及其实现。并且仍然需要序列化程序的作者和序列化程序的消费者都付出一些精神上的努力。

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

于 2012-11-26T11:36:03.713 回答
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
}

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

于 2012-11-26T10:12:40.170 回答