C#将对象序列化到文件时的最佳实践

C#将对象序列化到文件时的最佳实践,c#,serialization,C#,Serialization,我正在构建一个小应用程序,它需要将一个对象保存到一个文件中,以便保存用户数据。 我对此文件的序列化有两个问题: 我正在创建的对象有一些公共属性和一个事件。我向我的对象添加了[Serializable]属性,然后意识到我无法序列化包含事件的对象。 然后我发现我可以在我的事件[field:NonSerialized]上面添加一个属性,它就可以工作了。这是最好的方法吗,还是应该尝试在没有任何事件的情况下构建可序列化对象 我正在序列化的对象保存了一些有关应用程序的用户设置。这些设置不够敏感,无法在文件中

我正在构建一个小应用程序,它需要将一个对象保存到一个文件中,以便保存用户数据。 我对此文件的序列化有两个问题:

  • 我正在创建的对象有一些公共属性和一个事件。我向我的对象添加了
    [Serializable]
    属性,然后意识到我无法序列化包含事件的对象。 然后我发现我可以在我的事件
    [field:NonSerialized]
    上面添加一个属性,它就可以工作了。这是最好的方法吗,还是应该尝试在没有任何事件的情况下构建可序列化对象

  • 我正在序列化的对象保存了一些有关应用程序的用户设置。这些设置不够敏感,无法在文件中进行加密,但我仍然不希望在不打开应用程序的情况下手动篡改它们。当我使用普通的
    BinaryFormatter
    对象,通过
    serialize()
    方法将对象序列化为文件时,我会在保存到的文件中看到.net对象类型的可读名称。有没有一种方法可以让某人对其进行反向工程,并在不使用我的程序的情况下查看所保存的内容?有没有一种方法可以让某人构建一个小型应用程序,并了解如何反序列化此文件中的信息?如果是这样,我将如何在该文件中隐藏信息

  • 在这种情况下,在将对象序列化到文件时,是否还有其他的提示/建议/最佳实践值得我坚持


    提前谢谢

    如果对象实现了接口,您可以自己控制存储/序列化的所有数据,并且可以控制反序列化

    如果您的项目能够及时发展,这一点很重要。因为您可能会删除某些属性、添加其他属性或更改行为

    我总是在序列化包中添加一个版本。通过这种方式,我知道存储对象时它的版本是什么,因此我知道如何对其进行反序列化

    [Serializable]
    class Example : ISerializable {
       private static const int VERSION = 3;
    
       public Example(SerializationInfo info, StreamingContext context) {
          var version = info.GetInt32("Example_Version", VERSION);
          if (version == 0) {
             // Restore properties for version 0
          }
          if (version == 1) {
             // ....
          }
       }
    
       void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
           info.AddValue("Example_Version", VERSION);
           // Your data here
       }
    
    }
    
    如果你不加密,它将很容易“读取”你的数据。非常简单,这意味着你可能需要投入几个小时。如果你存储的数据值几天,这意味着它很容易,如果只值几分钟,那就很难了。如果你明白了


    加密数据的一种非常简单的方法是通过类使用Windows DPAPI。

    1:使用BinaryFormatter,是的-事件需要非序列化(除非实现ISerializable,但这会增加大量工作);然而,我在记录中说我在这里不会使用BinaryFormatter。对你的类型进行一系列的更改是不太可以原谅的。我会使用一些与代码内部联系较少的东西;XmlSerializer;DataContractSerializer,JavaScriptSerializer。我也可以建议二元选择;NetDataContractSerializer、protobuf net(我自己的)等

    2:是的,对于几乎所有不涉及正确加密的实现,如果有人关心的话,他们可以进行反向工程并获得字符串。所以这取决于它需要隐藏的程度。只需通过GZipStream运行现有的序列化就足以满足您的需要,但这只是一个防止随意检查的面具。它不会阻止任何有理由寻找数据的人

    如果数据需要安全,您需要使用用户在应用程序启动时输入的密钥或类似证书之类的东西对其用户配置文件进行适当的加密

  • 我将从对象中删除事件。那样比较干净

  • 任何东西都可以逆向工程。保存文件时只需对其进行加密。这很容易做到。当然,加密密钥必须存储在应用程序的某个地方,因此,除非你混淆了你的代码,否则坚定的黑客将能够找到它


  • 你能告诉我(如果一个示例太长,可能会有一篇好文章的链接)如何实现ISerializable,以确定要序列化哪些属性吗?在哪里跟踪每个序列化对象的版本?添加了一个示例。还要遵循ISerializable链接,这也是一个很好的示例。如您所见,该版本只是ISerializable实现的一个简单变量/const.+1。如果要将数据保存到文件中,那么版本公差几乎是必需的。此外,此代码提供了实现版本容忍序列化的最透明的方式(可读性)。即使应用程序被混淆,一个坚定的黑客也会找到加密密钥。这很难做到,但仍然可行。这适用于任何语言和框架,而不仅仅是.NET。@Paul Sasik:你完全正确。在一天结束时,只要密钥是客户端的,那么就无法完全停止对它的访问。如果我使用XmlSerializer进行序列化,然后在将来我的对象发生更改,我猜这仍然会导致反序列化错误?使用XmlSerializable,我不需要为事件添加非序列化?@gillyb XmlSerializer忽略事件,只查看公共属性/字段(但是:不要使用公共字段)。您可以将整个类型系统交换为日志,因为属性仍然有意义。我不明白您所说的“只要属性仍然有意义就交换整个类型系统”???在我的情况下,对象im序列化实现了INotifyPropertyChanged,所以我需要一个公共事件来正确实现这一点。这有什么不好吗?