如果旧版本的 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
),那么我怎样才能最好地做到这一点?
我研究了 SerializationBinder
和 ISerializationSurrogate
但不知道如何使用它们来解决我的问题。
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.
发布评论
评论(1)
我已经找到了我自己问题的部分答案。检查调试器中
SerializationInfo
的MemberNames
和MemberValues
属性会显示存储在其中的成员的类型。Dictionary
作为名称包含在SerializationInfo
中的项目,名称为KeyValuePairs
且类型为array> ^。有了这些信息,
SerializationInfo
的GetValue
方法就可以用来检索键值对,然后可以将它们转换并添加到正在填充的对象中。 SerializationBinder 可用于让一个类的反序列化构造函数也处理另一个类的反序列化,从而允许在重命名类后向后兼容。以下代码显示了所有这些内容。运行此代码时,输出为:
遗憾的是,如果该类继承自
List
,则同样不起作用,因为List
未实现ISerializable
,因此__super::GetObjectData
调用在从List
派生的类中不可用。以下代码显示了我如何让它在小型应用程序中的List
中工作。该应用程序生成以下输出:
但是,当在非常大的应用程序中使用类似的代码来反序列化旧的、深层嵌套的数据时,我不断遇到
SerializationException
,附加信息仅限于“ID 为 < 的对象” em>数字在修复中被引用,但不存在。”,并且报告的小数字对我来说毫无意义。SerializationBinder
处理的类型名称的检查显示对
,反引号后面的数字不固定。这个数字是如何确定的?如果我添加其他类,我能否确定给定类的情况不会突然发生变化?
I've found a partial answer to my own questions. Inspection of the
MemberNames
andMemberValues
properties of theSerializationInfo
in the debugger shows the types of the members stored in there. ADictionary<X^, Y^>
is included in theSerializationInfo
as an item with nameKeyValuePairs
and typearray<System::Collections::Generic::KeyValuePair<X^, Y^>> ^
. With this information, theSerializationInfo
'sGetValue
method can be used to retrieve key-value pairs, and then they can be transformed and added to the object being filled. ASerializationBinder
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.When running this code, the output is:
Regrettably, the same does not work if the class inherits from a
List
, becauseList<T>
does not implementISerializable
, so the__super::GetObjectData
call is not available in a class derived fromList<T>
. The following code shows how I got it to work for aList
in a small application.This application generates the following output:
However, when using similar code in a very large application to deserialize old, deeply nested data, I keep running into
SerializationException
s 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 theSerializationBinder
showsas well as
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?