如何处理基于泛型集合的已更改类的反序列化向后兼容性?

发布于 2024-12-28 14:55:25 字数 2181 浏览 0 评论 0 原文

如果旧版本的 c++/CLI 应用程序序列化了从包含 X 类型键的字典派生的类 Foo,而新版本需要将键类型更改为 < code>Z 相反,那么如何才能最好地使应用程序支持读取旧的序列化数据(仍然基于 X)以及新的序列化数据(基于 Z )?

如果旧的情况是这样的:

ref class Foo: Generic::Dictionary<X^, Y^>, ISerializable
{
 public:
  Foo(SerializationInfo^ info, StreamingContext context)
  {
     info->AddValue("VERSION", 1);
     __super::GetObjectData(info, context);
  }

  virtual void GetObjectData(SerializationInfo^ info, StreamingContext context)
     : Generic::Dictionary<X^, Y^>(info, context)
  {
     int version = info->GetInt32("VERSION");
     /* omitted code to check version, act appropriately */
  } 
} 

那么在新的情况下我想做这样的事情:

ref class Foo: Generic::Dictionary<Z^, Y^>, ISerializable
{
 public:
  Foo(SerializationInfo^ info, StreamingContext context)
  {
    info->AddValue("VERSION", 2);
    __super::GetObjectData(info, context);
  }

  virtual void GetObjectData(SerializationInfo^ info, StreamingContext context)
  {
     int version = info->GetInt32("VERSION");
     if (version == 1)
     {
        Generic::Dictionary<X^, Y^> old 
          = gcnew Generic::Dictionary<X^, Y^>(info, context);
        /* code here to convert "old" to new format,
           assign to members of "this" */
     }
     else
     {
        Generic::Dictionary<Z^, Y^)(info, context);
     }
  }
}

但是失败并出现类型的编译错误:

错误 C2248:“System::Collections::Generic::Dictionary::Dictionary”:无法访问类“System::Collections::Generic::Dictionary'与 [ TKey=X ^, TValue=Y ^ ]

在更简单的情况下,我可以使用 info->GetValue 来提取和处理单个数据成员,但在当前情况下,字典的序列化留给了 .NET(通过 __super: :GetObjectData 调用),我不知道如何使用 info->GetValue 提取旧字典。

一个相关的问题:如果我想将 Foo 重命名为 BetterFoo 并且能够支持读取旧的序列化数据(仍然基于 Foo)以及新的序列化数据(基于 BetterFoo),那么我怎样才能最好地做到这一点?

我研究了 SerializationBinderISerializationSurrogate 但不知道如何使用它们来解决我的问题。

If the old version of a c++/CLI application serialized a class Foo derived from a Dictionary involving keys of type X, and the new version needs to change the key type to Z instead, then how can I best enable the application to support reading of old serialized data (still based on X) as well as new serialized data (based on Z)?

If the old situation is like this:

ref class Foo: Generic::Dictionary<X^, Y^>, ISerializable
{
 public:
  Foo(SerializationInfo^ info, StreamingContext context)
  {
     info->AddValue("VERSION", 1);
     __super::GetObjectData(info, context);
  }

  virtual void GetObjectData(SerializationInfo^ info, StreamingContext context)
     : Generic::Dictionary<X^, Y^>(info, context)
  {
     int version = info->GetInt32("VERSION");
     /* omitted code to check version, act appropriately */
  } 
} 

then in the new situation I'd like to do something like this:

ref class Foo: Generic::Dictionary<Z^, Y^>, ISerializable
{
 public:
  Foo(SerializationInfo^ info, StreamingContext context)
  {
    info->AddValue("VERSION", 2);
    __super::GetObjectData(info, context);
  }

  virtual void GetObjectData(SerializationInfo^ info, StreamingContext context)
  {
     int version = info->GetInt32("VERSION");
     if (version == 1)
     {
        Generic::Dictionary<X^, Y^> old 
          = gcnew Generic::Dictionary<X^, Y^>(info, context);
        /* code here to convert "old" to new format,
           assign to members of "this" */
     }
     else
     {
        Generic::Dictionary<Z^, Y^)(info, context);
     }
  }
}

but that fails with compilation errors of type :

error C2248: 'System::Collections::Generic::Dictionary<TKey,TValue>::Dictionary' : cannot access protected member declared in class 'System::Collections::Generic::Dictionary<TKey,TValue>' with [ TKey=X ^, TValue=Y ^ ].

In simpler cases, I can use info->GetValue to extract and process individual data members, but in the current case the serialization of the Dictionary was left to .NET (through the __super::GetObjectData call) and I don't know how to use info->GetValue to extract the old Dictionary.

A related question: If I want to rename Foo to BetterFoo and yet be able to support reading old serialized data (still based on Foo) as well as new serialized data (based on BetterFoo), then how can I best do that?

I've looked into SerializationBinder and ISerializationSurrogate but couldn't figure out how to use them to solve my problems.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

海未深 2025-01-04 14:55:25

我已经找到了我自己问题的部分答案。检查调试器中 SerializationInfoMemberNamesMemberValues 属性会显示存储在其中的成员的类型。 Dictionary 作为名称包含在 SerializationInfo 中的项目,名称为 KeyValuePairs 且类型为 array> ^。有了这些信息,SerializationInfoGetValue 方法就可以用来检索键值对,然后可以将它们转换并添加到正在填充的对象中。 SerializationBinder 可用于让一个类的反序列化构造函数也处理另一个类的反序列化,从而允许在重命名类后向后兼容。以下代码显示了所有这些内容。

using namespace System;
using namespace System::IO;
using namespace System::Collections::Generic;
using namespace System::Runtime::Serialization;

typedef KeyValuePair<int, int> Foo1kvp;
[Serializable]
public ref class Foo1: Dictionary<int, int>, ISerializable
{
public:
    Foo1() { }
    virtual void GetObjectData(SerializationInfo^ info, StreamingContext context) override
    {
        info->AddValue("VERSION", 1);
        __super::GetObjectData(info, context);
    }
    Foo1(SerializationInfo^ info, StreamingContext context)
    {
        array<Foo1kvp>^ members = (array<Foo1kvp>^) info->GetValue("KeyValuePairs", array<Foo1kvp>::typeid);
        for each (Foo1kvp kvp in members)
        {
            this->Add(kvp.Key, kvp.Value);
        }
        Console::WriteLine("Deserializing Foo1");
    }
};

typedef KeyValuePair<String^, int> Foo2kvp;
[Serializable]
public ref class Foo2: Dictionary<String^, int>, ISerializable
{
public:
    Foo2() { }
    virtual void GetObjectData(SerializationInfo^ info, StreamingContext context) override
    {
        info->AddValue("VERSION", 2);
        __super::GetObjectData(info, context);
    }
    Foo2(SerializationInfo^ info, StreamingContext context)
    {
        int version = info->GetInt32("VERSION");
        if (version == 1)
        {
            array<Foo1kvp>^ members = (array<Foo1kvp>^) info->GetValue("KeyValuePairs", array<Foo1kvp>::typeid);
            for each (Foo1kvp kvp in members)
            {
                this->Add(kvp.Key.ToString(), kvp.Value);
            }
            Console::WriteLine("Deserializing Foo2 from Foo1");
        }
        else
        {
            array<Foo2kvp>^ members = (array<Foo2kvp>^) info->GetValue("KeyValuePairs", array<Foo2kvp>::typeid);
            for each (Foo2kvp kvp in members)
            {
                this->Add(kvp.Key, kvp.Value);
            }
            Console::WriteLine("Deserializing Foo2");
        }
    }
};

ref class MyBinder sealed: public SerializationBinder
{
public:
    virtual Type^ BindToType(String^ assemblyName, String^ typeName) override
    {
        if (typeName == "Foo1")
            typeName = "Foo2";
        return Type::GetType(String::Format("{0}, {1}", typeName, assemblyName));
    }
};

int main(array<System::String ^> ^args)
{
    Console::WriteLine(L"Hello World");
    Foo1^ foo1 = gcnew Foo1;
    foo1->Add(2, 7);
    foo1->Add(3, 5);

    IFormatter^ formatter1 = gcnew Formatters::Binary::BinaryFormatter(); // no translation to Foo2
    IFormatter^ formatter2 = gcnew Formatters::Binary::BinaryFormatter();
    formatter2->Binder = gcnew MyBinder; // translate Foo1 to Foo2
    FileStream^ stream;
    try
    {
        // serialize Foo1
        stream = gcnew FileStream("fooserialized.dat", FileMode::Create, FileAccess::Write);
        formatter1->Serialize(stream, foo1);
        stream->Close();

        // deserialize Foo1 to Foo1
        stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
        Foo1^ foo1b = dynamic_cast<Foo1^>(formatter1->Deserialize(stream));
        stream->Close();
        Console::WriteLine("deserialized Foo1 from Foo1");
        for each (Foo1kvp kvp in foo1b)
        {
            Console::WriteLine("{0} -> {1}", kvp.Key, kvp.Value);
        }

        // deserialize Foo1 to Foo2
        stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
        Foo2^ foo2 = dynamic_cast<Foo2^>(formatter2->Deserialize(stream));
        stream->Close();
        Console::WriteLine("deserialized Foo2 from Foo1");
        for each (Foo2kvp kvp in foo2)
        {
            Console::WriteLine("{0} -> {1}", kvp.Key, kvp.Value);
        }

        // serialize Foo2
        Foo2^ foo2b = gcnew Foo2;
        foo2b->Add("Two", 7);
        foo2b->Add("Three", 5);
        stream = gcnew FileStream("fooserialized.dat", FileMode::Create, FileAccess::Write);
        formatter2->Serialize(stream, foo2b);
        stream->Close();

        // deserialize Foo2 to Foo2
        stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
        Foo2^ foo2c = dynamic_cast<Foo2^>(formatter2->Deserialize(stream));
        stream->Close();
        Console::WriteLine("deserialized Foo2 from Foo2");
        for each (Foo2kvp kvp in foo2c)
        {
            Console::WriteLine("{0} -> {1}", kvp.Key, kvp.Value);
        }
    }
    catch (Exception^ e)
    {
        Console::WriteLine(e);
        if (stream)
            stream->Close();
    }

    return 0;
}

运行此代码时,输​​出为:

Hello World
Deserializing Foo1
deserialized Foo1 from Foo1
2 -> 7
3 -> 5
Deserializing Foo2 from Foo1
deserialized Foo2 from Foo1
2 -> 7
3 -> 5
Deserializing Foo2
deserialized Foo2 from Foo2
Two -> 7
Three -> 5

遗憾的是,如果该类继承自 List,则同样不起作用,因为 List 未实现 ISerializable ,因此 __super::GetObjectData 调用在从 List 派生的类中不可用。以下代码显示了我如何让它在小型应用程序中的 List 中工作。

using namespace System;
using namespace System::IO;
using namespace System::Collections::Generic;
using namespace System::Runtime::Serialization;

[Serializable]
public ref class Foo1: List<int>
{ };

int
OurVersionNumber(SerializationInfo^ info)
{
   // Serialized Foo1 has no VERSION property, but Foo2 does have it.
   // Don't use info->GetInt32("VERSION") in a try-catch statement,
   // because that is *very* slow when corresponding
   // SerializationExceptions are triggered in the debugger.
   SerializationInfoEnumerator^ it = info->GetEnumerator();
   int version = 1;
   while (it->MoveNext())
   {
      if (it->Name == "VERSION")
      {
         version = (Int32) it->Value;
         break;
      }
   }
   return version;
}

[Serializable]
public ref class Foo2: List<String^>, ISerializable
{
public:
  Foo2() { }

  // NOTE: no "override" on this one, because List<T> doesn't provide this method
  virtual void GetObjectData(SerializationInfo^ info, StreamingContext context)
  {
    info->AddValue("VERSION", 2);
    int size = this->Count;
    List<String^>^ list = gcnew List<String^>(this);
    info->AddValue("This", list);
  }
  Foo2(SerializationInfo^ info, StreamingContext context)
  {
    int version = OurVersionNumber(info);
    if (version == 1)
    {
      int size = info->GetInt32("List`1+_size");
      array<int>^ members = (array<int>^) info->GetValue("List`1+_items", array<int>::typeid);
      for each (int value in members)
      {
        if (!size--)
          break; // done; the remaining 'members' slots are empty
        this->Add(value.ToString());
      }
      Console::WriteLine("Deserializing Foo2 from Foo1");
    }
    else
    {
      List<String^>^ list = (List<String^>^) info->GetValue("This", List<String^>::typeid);
      int size = list->Count;
      this->AddRange(list);
      size = this->Count;
      Console::WriteLine("Deserializing Foo2");
    }
  }
};

ref class MyBinder sealed: public SerializationBinder
{
public:
  virtual Type^ BindToType(String^ assemblyName, String^ typeName) override
  {
    if (typeName == "Foo1")
      typeName = "Foo2";
    return Type::GetType(String::Format("{0}, {1}", typeName, assemblyName));
  }
};

int main(array<System::String ^> ^args)
{
  Console::WriteLine(L"Hello World");
  Foo1^ foo1 = gcnew Foo1;
  foo1->Add(2);
  foo1->Add(3);

  IFormatter^ formatter1 = gcnew Formatters::Binary::BinaryFormatter(); // no translation to Foo2
  IFormatter^ formatter2 = gcnew Formatters::Binary::BinaryFormatter();
  formatter2->Binder = gcnew MyBinder; // translate Foo1 to Foo2
  FileStream^ stream;
  try
  {
    // serialize Foo1
    stream = gcnew FileStream("fooserialized.dat", FileMode::Create, FileAccess::Write);
    formatter1->Serialize(stream, foo1);
    stream->Close();

    // deserialize Foo1 to Foo1
    stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
    Foo1^ foo1b = (Foo1^) formatter1->Deserialize(stream);
    stream->Close();
    Console::WriteLine("deserialized Foo1 from Foo1");
    for each (int value in foo1b)
    {
      Console::WriteLine(value);
    }

    // deserialize Foo1 to Foo2
    stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
    Foo2^ foo2 = (Foo2^) formatter2->Deserialize(stream);
    stream->Close();
    Console::WriteLine("deserialized Foo2 from Foo1");
    for each (String^ value in foo2)
    {
      Console::WriteLine(value);
    }

    // serialize Foo2
    Foo2^ foo2b = gcnew Foo2;
    foo2b->Add("Two");
    foo2b->Add("Three");
    stream = gcnew FileStream("fooserialized.dat", FileMode::Create, FileAccess::Write);
    formatter2->Serialize(stream, foo2b);
    stream->Close();

    // deserialize Foo2 to Foo2
    stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
    Foo2^ foo2c = (Foo2^) formatter2->Deserialize(stream);
    int size = foo2c->Count;
    stream->Close();
    Console::WriteLine("deserialized Foo2 from Foo2");
    for each (String^ value in foo2c)
    {
      Console::WriteLine(value);
    }
  }
  catch (Exception^ e)
  {
    Console::WriteLine(e);
    if (stream)
      stream->Close();
  }

  return 0;
}

该应用程序生成以下输出:

Hello World
deserialized Foo1 from Foo1
2
3
Deserializing Foo2 from Foo1
deserialized Foo2 from Foo1
2
3
Deserializing Foo2
deserialized Foo2 from Foo2
Two
Three

但是,当在非常大的应用程序中使用类似的代码来反序列化旧的、深层嵌套的数据时,我不断遇到 SerializationException ,附加信息仅限于“ID 为 < 的对象” em>数字在修复中被引用,但不存在。”,并且报告的小数字对我来说毫无意义。 SerializationBinder 处理的类型名称的检查显示

System.Collections.Generic.KeyValuePair`2

System.Collections.Generic.List`1

,反引号后面的数字不固定。这个数字是如何确定的?如果我添加其他类,我能否确定给定类的情况不会突然发生变化?

I've found a partial answer to my own questions. Inspection of the MemberNames and MemberValues properties of the SerializationInfo in the debugger shows the types of the members stored in there. A Dictionary<X^, Y^> is included in the SerializationInfo as an item with name KeyValuePairs and type array<System::Collections::Generic::KeyValuePair<X^, Y^>> ^. With this information, the SerializationInfo's GetValue method can be used to retrieve key-value pairs, and then they can be transformed and added to the object being filled. A SerializationBinder can be used to have the deserialization constructor of one class handle also the deserialization of another class, thus allowing backward compatibility after renaming a class. The following code shows all of these things.

using namespace System;
using namespace System::IO;
using namespace System::Collections::Generic;
using namespace System::Runtime::Serialization;

typedef KeyValuePair<int, int> Foo1kvp;
[Serializable]
public ref class Foo1: Dictionary<int, int>, ISerializable
{
public:
    Foo1() { }
    virtual void GetObjectData(SerializationInfo^ info, StreamingContext context) override
    {
        info->AddValue("VERSION", 1);
        __super::GetObjectData(info, context);
    }
    Foo1(SerializationInfo^ info, StreamingContext context)
    {
        array<Foo1kvp>^ members = (array<Foo1kvp>^) info->GetValue("KeyValuePairs", array<Foo1kvp>::typeid);
        for each (Foo1kvp kvp in members)
        {
            this->Add(kvp.Key, kvp.Value);
        }
        Console::WriteLine("Deserializing Foo1");
    }
};

typedef KeyValuePair<String^, int> Foo2kvp;
[Serializable]
public ref class Foo2: Dictionary<String^, int>, ISerializable
{
public:
    Foo2() { }
    virtual void GetObjectData(SerializationInfo^ info, StreamingContext context) override
    {
        info->AddValue("VERSION", 2);
        __super::GetObjectData(info, context);
    }
    Foo2(SerializationInfo^ info, StreamingContext context)
    {
        int version = info->GetInt32("VERSION");
        if (version == 1)
        {
            array<Foo1kvp>^ members = (array<Foo1kvp>^) info->GetValue("KeyValuePairs", array<Foo1kvp>::typeid);
            for each (Foo1kvp kvp in members)
            {
                this->Add(kvp.Key.ToString(), kvp.Value);
            }
            Console::WriteLine("Deserializing Foo2 from Foo1");
        }
        else
        {
            array<Foo2kvp>^ members = (array<Foo2kvp>^) info->GetValue("KeyValuePairs", array<Foo2kvp>::typeid);
            for each (Foo2kvp kvp in members)
            {
                this->Add(kvp.Key, kvp.Value);
            }
            Console::WriteLine("Deserializing Foo2");
        }
    }
};

ref class MyBinder sealed: public SerializationBinder
{
public:
    virtual Type^ BindToType(String^ assemblyName, String^ typeName) override
    {
        if (typeName == "Foo1")
            typeName = "Foo2";
        return Type::GetType(String::Format("{0}, {1}", typeName, assemblyName));
    }
};

int main(array<System::String ^> ^args)
{
    Console::WriteLine(L"Hello World");
    Foo1^ foo1 = gcnew Foo1;
    foo1->Add(2, 7);
    foo1->Add(3, 5);

    IFormatter^ formatter1 = gcnew Formatters::Binary::BinaryFormatter(); // no translation to Foo2
    IFormatter^ formatter2 = gcnew Formatters::Binary::BinaryFormatter();
    formatter2->Binder = gcnew MyBinder; // translate Foo1 to Foo2
    FileStream^ stream;
    try
    {
        // serialize Foo1
        stream = gcnew FileStream("fooserialized.dat", FileMode::Create, FileAccess::Write);
        formatter1->Serialize(stream, foo1);
        stream->Close();

        // deserialize Foo1 to Foo1
        stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
        Foo1^ foo1b = dynamic_cast<Foo1^>(formatter1->Deserialize(stream));
        stream->Close();
        Console::WriteLine("deserialized Foo1 from Foo1");
        for each (Foo1kvp kvp in foo1b)
        {
            Console::WriteLine("{0} -> {1}", kvp.Key, kvp.Value);
        }

        // deserialize Foo1 to Foo2
        stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
        Foo2^ foo2 = dynamic_cast<Foo2^>(formatter2->Deserialize(stream));
        stream->Close();
        Console::WriteLine("deserialized Foo2 from Foo1");
        for each (Foo2kvp kvp in foo2)
        {
            Console::WriteLine("{0} -> {1}", kvp.Key, kvp.Value);
        }

        // serialize Foo2
        Foo2^ foo2b = gcnew Foo2;
        foo2b->Add("Two", 7);
        foo2b->Add("Three", 5);
        stream = gcnew FileStream("fooserialized.dat", FileMode::Create, FileAccess::Write);
        formatter2->Serialize(stream, foo2b);
        stream->Close();

        // deserialize Foo2 to Foo2
        stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
        Foo2^ foo2c = dynamic_cast<Foo2^>(formatter2->Deserialize(stream));
        stream->Close();
        Console::WriteLine("deserialized Foo2 from Foo2");
        for each (Foo2kvp kvp in foo2c)
        {
            Console::WriteLine("{0} -> {1}", kvp.Key, kvp.Value);
        }
    }
    catch (Exception^ e)
    {
        Console::WriteLine(e);
        if (stream)
            stream->Close();
    }

    return 0;
}

When running this code, the output is:

Hello World
Deserializing Foo1
deserialized Foo1 from Foo1
2 -> 7
3 -> 5
Deserializing Foo2 from Foo1
deserialized Foo2 from Foo1
2 -> 7
3 -> 5
Deserializing Foo2
deserialized Foo2 from Foo2
Two -> 7
Three -> 5

Regrettably, the same does not work if the class inherits from a List, because List<T> does not implement ISerializable, so the __super::GetObjectData call is not available in a class derived from List<T>. The following code shows how I got it to work for a List in a small application.

using namespace System;
using namespace System::IO;
using namespace System::Collections::Generic;
using namespace System::Runtime::Serialization;

[Serializable]
public ref class Foo1: List<int>
{ };

int
OurVersionNumber(SerializationInfo^ info)
{
   // Serialized Foo1 has no VERSION property, but Foo2 does have it.
   // Don't use info->GetInt32("VERSION") in a try-catch statement,
   // because that is *very* slow when corresponding
   // SerializationExceptions are triggered in the debugger.
   SerializationInfoEnumerator^ it = info->GetEnumerator();
   int version = 1;
   while (it->MoveNext())
   {
      if (it->Name == "VERSION")
      {
         version = (Int32) it->Value;
         break;
      }
   }
   return version;
}

[Serializable]
public ref class Foo2: List<String^>, ISerializable
{
public:
  Foo2() { }

  // NOTE: no "override" on this one, because List<T> doesn't provide this method
  virtual void GetObjectData(SerializationInfo^ info, StreamingContext context)
  {
    info->AddValue("VERSION", 2);
    int size = this->Count;
    List<String^>^ list = gcnew List<String^>(this);
    info->AddValue("This", list);
  }
  Foo2(SerializationInfo^ info, StreamingContext context)
  {
    int version = OurVersionNumber(info);
    if (version == 1)
    {
      int size = info->GetInt32("List`1+_size");
      array<int>^ members = (array<int>^) info->GetValue("List`1+_items", array<int>::typeid);
      for each (int value in members)
      {
        if (!size--)
          break; // done; the remaining 'members' slots are empty
        this->Add(value.ToString());
      }
      Console::WriteLine("Deserializing Foo2 from Foo1");
    }
    else
    {
      List<String^>^ list = (List<String^>^) info->GetValue("This", List<String^>::typeid);
      int size = list->Count;
      this->AddRange(list);
      size = this->Count;
      Console::WriteLine("Deserializing Foo2");
    }
  }
};

ref class MyBinder sealed: public SerializationBinder
{
public:
  virtual Type^ BindToType(String^ assemblyName, String^ typeName) override
  {
    if (typeName == "Foo1")
      typeName = "Foo2";
    return Type::GetType(String::Format("{0}, {1}", typeName, assemblyName));
  }
};

int main(array<System::String ^> ^args)
{
  Console::WriteLine(L"Hello World");
  Foo1^ foo1 = gcnew Foo1;
  foo1->Add(2);
  foo1->Add(3);

  IFormatter^ formatter1 = gcnew Formatters::Binary::BinaryFormatter(); // no translation to Foo2
  IFormatter^ formatter2 = gcnew Formatters::Binary::BinaryFormatter();
  formatter2->Binder = gcnew MyBinder; // translate Foo1 to Foo2
  FileStream^ stream;
  try
  {
    // serialize Foo1
    stream = gcnew FileStream("fooserialized.dat", FileMode::Create, FileAccess::Write);
    formatter1->Serialize(stream, foo1);
    stream->Close();

    // deserialize Foo1 to Foo1
    stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
    Foo1^ foo1b = (Foo1^) formatter1->Deserialize(stream);
    stream->Close();
    Console::WriteLine("deserialized Foo1 from Foo1");
    for each (int value in foo1b)
    {
      Console::WriteLine(value);
    }

    // deserialize Foo1 to Foo2
    stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
    Foo2^ foo2 = (Foo2^) formatter2->Deserialize(stream);
    stream->Close();
    Console::WriteLine("deserialized Foo2 from Foo1");
    for each (String^ value in foo2)
    {
      Console::WriteLine(value);
    }

    // serialize Foo2
    Foo2^ foo2b = gcnew Foo2;
    foo2b->Add("Two");
    foo2b->Add("Three");
    stream = gcnew FileStream("fooserialized.dat", FileMode::Create, FileAccess::Write);
    formatter2->Serialize(stream, foo2b);
    stream->Close();

    // deserialize Foo2 to Foo2
    stream = gcnew FileStream("fooserialized.dat", FileMode::Open, FileAccess::Read);
    Foo2^ foo2c = (Foo2^) formatter2->Deserialize(stream);
    int size = foo2c->Count;
    stream->Close();
    Console::WriteLine("deserialized Foo2 from Foo2");
    for each (String^ value in foo2c)
    {
      Console::WriteLine(value);
    }
  }
  catch (Exception^ e)
  {
    Console::WriteLine(e);
    if (stream)
      stream->Close();
  }

  return 0;
}

This application generates the following output:

Hello World
deserialized Foo1 from Foo1
2
3
Deserializing Foo2 from Foo1
deserialized Foo2 from Foo1
2
3
Deserializing Foo2
deserialized Foo2 from Foo2
Two
Three

However, when using similar code in a very large application to deserialize old, deeply nested data, I keep running into SerializationExceptions with additional information limited to "The object with ID number was referenced in a fixup but does not exist.", and the reported small number is meaningless to me. Inspection of the type names processed by the SerializationBinder shows

System.Collections.Generic.KeyValuePair`2

as well as

System.Collections.Generic.List`1

so the number after the backtick is not fixed. How is that number determined? Can I be sure that it won't suddenly change for a given class if I add other classes to the mix?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文