;, (emp1 == emp3));
/*5d*/ Console::WriteLine("list[0] == list[1] is {0}", (list[0] == list[1]));
/*5e*/ Console::WriteLine("list[0] == emp1 is {0}", (list[0] == emp1));
/*5f*/ Console::WriteLine("list[1] == emp1 is {0}", (list[1] == emp1));
}
在本例中,我们想对Employee类型(在标记1中的用户自定义类型)的对象进行串行化,必须把Serializable属性附加到这个类型上。如果我们试图串行化一个没有标明此属性的类对象,将会抛出一个System::Runtime::Serialization::SerializationException类型的异常。串行化之前的程序输出如插3所示:
插3:串行化之前例4的输出
emp1 == emp2 is False
emp2 == emp3 is True
emp1 == emp3 is False
list[0] == list[1] is True
list[0] == emp1 is True
list[1] == emp1 is True
我们对四个目标进行了串行化,前两个代表了不同的Employee对象,而第三个是对第二个的引用,第四个为包含两个元素的数组,这两个元素均引用第一个Employee对象。程序的输出表明了它们之间的这些关系,反串行化之后的输出见插4:
插4:反串行化之后例4的输出
emp1 == emp2 is False
emp2 == emp3 is False
emp1 == emp3 is False
list[0] == list[1] is True
list[0] == emp1 is False
list[1] == emp1 is False
注意,现在第三个Employee句柄已不再是一个指向第二个Employee对象的句柄了,类似地,尽管list[0]与list[1]都引用同一个Empolyee对象,但对象已不是我们取回的第一个对象了。
在此应看到,当多个对象逐个串行化之后,它们是相关联的,而当反串行化之后,它们的关系并没有因此而恢复,但是,对象内部的关系仍然被维持。
自定义的串行化
默认情况下,当一个对象被串行化时,所有的非静态实例字段都会被写入,并在反串行化期间顺序读回;然而,对包含静态字段的类,这可能会导致一个问题。
在例5中使用了Point类,其不但包含了用于追踪每个Point x与y坐标的实例变量,而且还会跟踪在程序执行期间创建的Point数目。例如,在例5中,通过显示构造函数调用,创建了4个Point,并将它们串行化到磁盘;当它们被反串行化时,又创建了4个新的Point,因此Point总数现在为8,插5中是程序的输出:
例5:
using namespace System;
using namespace System::IO;
using namespace System::Runtime::Serialization::Formatters::Binary;
int main()
{
Console::WriteLine("PointCount: {0}", Point::PointCount);
Point^ p1 = gcnew Point(15, 10);
Point^ p2 = gcnew Point(-2, 12);
array<Point^>^ p3 = {gcnew Point(18, -5), gcnew Point(25, 19)};
Console::WriteLine("PointCount: {0}", Point::PointCount);
BinaryFormatter^ formatter = gcnew BinaryFormatter;
Stream^ file = File::Open("Point.ser", FileMode::Create);
formatter->Serialize(file, p1);
formatter->Serialize(file, p2);
formatter->Serialize(file, p3);
file->Close();
file = File::Open("Point.ser", FileMode::Open);
Point^ p4 = static_cast<Point^>(formatter->Deserialize(file));
Console::WriteLine("PointCount: {0}", Point::PointCount);
Point^ p5 = static_cast<Point^>(formatter->Deserialize(file));
Console::WriteLine("PointCount: {0}", Point::PointCount);
array<Point^>^ p6 = static_cast<array<Point^>^>(formatter->Deserialize(file));
Console::WriteLine("PointCount: {0}", Point::PointCount);
file->Close();
Console::WriteLine("p1: {0}, p4: {1}", p1, p4);
Console::WriteLine("p2: {0}, p5: {1}", p2, p5);
Console::WriteLine("p3[0]: {0}, p6[0]: {1}", p3[0], p6[0]);
Console::WriteLine("p3[1]: {0}, p6[1]: {1}", p3[1], p6[1]);
}
插5:反串行化创建了4个新的Point
PointCount: 0
PointCount: 4
PointCount: 5
PointCount: 6
PointCount: 8
p1: (15,10), p4: (15,10)
p2: (-2,12), p5: (-2,12)
p3[0]: (18,-5), p6[0]: (18,-5)
p3[1]: (25,19), p6[1]: (25,19)
当调用Point类的公有构造函数来构造一个新对象时,Point计数字段也会相应增长;而当我们反串行化一个或多个Point时,问题发生了,对Point的Deserialize调用实际上创建了一个新的Point对象,但它并没有为这些对象调用任何的构造函数啊。另外要明确一点,即使新Point数被增量1 ,PointCount也不会自动增长。我们可重载由接口ISerializable(从System::Runtime::Serialization)实现的默认的串行与反串行动作;这个接口需要定义一个调用GetObjectData的函数,这个函数就可以允许我们重载串行化过程。
GetObjectData函数的目的是,以串行化一个父类对象所需的数据,增加一个SerializationInfo对象,在此,名称、值、类型信息都被提供给AddValue函数,并由对象作为第二个参数。名称字符串可为任意,只要它在这种类型的串行化中唯一就行了。(如果使用了两个相同的名称,会抛出SerializationException异常。)
如果要重载反串行化过程,必须定义另一个构造函数,注意这个构造为私有类型,因为它只会被反串行化机制所调用,没有从外部访问的必要。
串行化的格式
以上所有串行化的例子当中,我们使用了BinaryFormatter类型,其以某种能被高效地处理的压缩格式来存储数据;然而,其他格式也能做到这点,例如,可使用一个SOAP,SOAP(Simple Object Access Protocol--简单对象访问协议)是一种用于在Web上交换结构化及类型信息的简单的、基于XML的协议,该协议未包含任何应用程序或传输语义,所以它具有高度模块化及扩展性的特点。当然,大家也能创建其他的自定义格式。
/*5d*/ Console::WriteLine("list[0] == list[1] is {0}", (list[0] == list[1]));
/*5e*/ Console::WriteLine("list[0] == emp1 is {0}", (list[0] == emp1));
/*5f*/ Console::WriteLine("list[1] == emp1 is {0}", (list[1] == emp1));
}
在本例中,我们想对Employee类型(在标记1中的用户自定义类型)的对象进行串行化,必须把Serializable属性附加到这个类型上。如果我们试图串行化一个没有标明此属性的类对象,将会抛出一个System::Runtime::Serialization::SerializationException类型的异常。串行化之前的程序输出如插3所示:
插3:串行化之前例4的输出
emp1 == emp2 is False
emp2 == emp3 is True
emp1 == emp3 is False
list[0] == list[1] is True
list[0] == emp1 is True
list[1] == emp1 is True
我们对四个目标进行了串行化,前两个代表了不同的Employee对象,而第三个是对第二个的引用,第四个为包含两个元素的数组,这两个元素均引用第一个Employee对象。程序的输出表明了它们之间的这些关系,反串行化之后的输出见插4:
插4:反串行化之后例4的输出
emp1 == emp2 is False
emp2 == emp3 is False
emp1 == emp3 is False
list[0] == list[1] is True
list[0] == emp1 is False
list[1] == emp1 is False
注意,现在第三个Employee句柄已不再是一个指向第二个Employee对象的句柄了,类似地,尽管list[0]与list[1]都引用同一个Empolyee对象,但对象已不是我们取回的第一个对象了。
在此应看到,当多个对象逐个串行化之后,它们是相关联的,而当反串行化之后,它们的关系并没有因此而恢复,但是,对象内部的关系仍然被维持。
自定义的串行化
默认情况下,当一个对象被串行化时,所有的非静态实例字段都会被写入,并在反串行化期间顺序读回;然而,对包含静态字段的类,这可能会导致一个问题。
在例5中使用了Point类,其不但包含了用于追踪每个Point x与y坐标的实例变量,而且还会跟踪在程序执行期间创建的Point数目。例如,在例5中,通过显示构造函数调用,创建了4个Point,并将它们串行化到磁盘;当它们被反串行化时,又创建了4个新的Point,因此Point总数现在为8,插5中是程序的输出:
例5:
using namespace System;
using namespace System::IO;
using namespace System::Runtime::Serialization::Formatters::Binary;
int main()
{
Console::WriteLine("PointCount: {0}", Point::PointCount);
Point^ p1 = gcnew Point(15, 10);
Point^ p2 = gcnew Point(-2, 12);
array<Point^>^ p3 = {gcnew Point(18, -5), gcnew Point(25, 19)};
Console::WriteLine("PointCount: {0}", Point::PointCount);
BinaryFormatter^ formatter = gcnew BinaryFormatter;
Stream^ file = File::Open("Point.ser", FileMode::Create);
formatter->Serialize(file, p1);
formatter->Serialize(file, p2);
formatter->Serialize(file, p3);
file->Close();
file = File::Open("Point.ser", FileMode::Open);
Point^ p4 = static_cast<Point^>(formatter->Deserialize(file));
Console::WriteLine("PointCount: {0}", Point::PointCount);
Point^ p5 = static_cast<Point^>(formatter->Deserialize(file));
Console::WriteLine("PointCount: {0}", Point::PointCount);
array<Point^>^ p6 = static_cast<array<Point^>^>(formatter->Deserialize(file));
Console::WriteLine("PointCount: {0}", Point::PointCount);
file->Close();
Console::WriteLine("p1: {0}, p4: {1}", p1, p4);
Console::WriteLine("p2: {0}, p5: {1}", p2, p5);
Console::WriteLine("p3[0]: {0}, p6[0]: {1}", p3[0], p6[0]);
Console::WriteLine("p3[1]: {0}, p6[1]: {1}", p3[1], p6[1]);
}
插5:反串行化创建了4个新的Point
PointCount: 0
PointCount: 4
PointCount: 5
PointCount: 6
PointCount: 8
p1: (15,10), p4: (15,10)
p2: (-2,12), p5: (-2,12)
p3[0]: (18,-5), p6[0]: (18,-5)
p3[1]: (25,19), p6[1]: (25,19)
当调用Point类的公有构造函数来构造一个新对象时,Point计数字段也会相应增长;而当我们反串行化一个或多个Point时,问题发生了,对Point的Deserialize调用实际上创建了一个新的Point对象,但它并没有为这些对象调用任何的构造函数啊。另外要明确一点,即使新Point数被增量1 ,PointCount也不会自动增长。我们可重载由接口ISerializable(从System::Runtime::Serialization)实现的默认的串行与反串行动作;这个接口需要定义一个调用GetObjectData的函数,这个函数就可以允许我们重载串行化过程。
GetObjectData函数的目的是,以串行化一个父类对象所需的数据,增加一个SerializationInfo对象,在此,名称、值、类型信息都被提供给AddValue函数,并由对象作为第二个参数。名称字符串可为任意,只要它在这种类型的串行化中唯一就行了。(如果使用了两个相同的名称,会抛出SerializationException异常。)
如果要重载反串行化过程,必须定义另一个构造函数,注意这个构造为私有类型,因为它只会被反串行化机制所调用,没有从外部访问的必要。
串行化的格式
以上所有串行化的例子当中,我们使用了BinaryFormatter类型,其以某种能被高效地处理的压缩格式来存储数据;然而,其他格式也能做到这点,例如,可使用一个SOAP,SOAP(Simple Object Access Protocol--简单对象访问协议)是一种用于在Web上交换结构化及类型信息的简单的、基于XML的协议,该协议未包含任何应用程序或传输语义,所以它具有高度模块化及扩展性的特点。当然,大家也能创建其他的自定义格式。

【