实际应用中,往往需要将Voucher批量保存到磁盘文件,然后发送到业务中心。可以将每个Voucher保存为一个文件,然后将多个文件顺序传输,但这并不是一种很好的方案。
BinnaryFormatter的Serialize方法需要传入一个Stream,这个Stream应当是可以持续写入的,因此将多个Voucher写入同一个stream中,应该是没有问题的。VoucherSerializer的BatchSerialize和BatchDeserialize方法的代码如下:
本文的例子使用的是FileStream,因此在Deserialize的代码中使用 fs.Position != fs.Length代表是否已读到文件尾,但在其他的Stream中,比如NetworkStream,这一逻辑需要适当修改。
NUnit对应的测试代码如下:
TestSerializeVouchers中使用了两个相同的Voucher对象测试,这样做的目的是想分析生成的voucher.data文件的内容:
因为接口没有变化,因此可以用之前的NUnit测试方法,测试通过。因此,可以得出结论, 如果类A能够被序列化,那么A[]也能被序列化。
NUnit测试方法序列化Voucher[]时生成的二进制文件内容如下:
为了比较两种序列化的方式,把多个对象依次序列化产生的文件内容列在下面:
采用序列化数组的方法产生的文件要比将多个对象依次序列化产生的文件要小得多。数组序列化时,Assembly的信息,Voucher class的信息,Voucher fields的信息都只需要一次输出,而将多个对象依次序列化时,相应的信息要为每一个对象输出。
除了类型信息之外,还有一个有意思的现象,"2005012900001"和"xingd"在数组序列化时只输出了一次,但NUnit的测试中是产生了两个Voucher对象的,虽然它们的voucherId和creator的值是一样的。.是不是因为值一样,所以才只输出一次呢。将测试方法修改如下:
输出的vouhcer.dat内容如下:
可以看到,两个Voucher对象的voucherId和creator都有输出了。但是,在这两种测试方法中,两个Voucher对象的createdTime都是分别输出的,但实际上它们应该具有相同的值。那么,是不是因为DateTime是值类型,而string是引用类型。NET中对字符串处理时,会产生一个内置的字符串池,具有相同值的字符串会引用值中的同一个对象。为了进一步了解,再对测试方法进行修改:
生成的文件内容如下:
明显的可以看出,只有一个Voucher对象产生了输出。 在BinaryFormatter序列化过程中,似乎是首先生成要序列化对象的整个Graph,然后将涉及的对象一一序列化的。因此,不仅是数组中相同的对象避免重复输出,在序列化对象所引用的对象形成的Graph中,相同的对象只会输出一次。
通过本文的比较可以确定,应当 优先考虑将集合类整体序列化,而不要自己将集合中的每一个元素依次序列化
BinnaryFormatter的Serialize方法需要传入一个Stream,这个Stream应当是可以持续写入的,因此将多个Voucher写入同一个stream中,应该是没有问题的。VoucherSerializer的BatchSerialize和BatchDeserialize方法的代码如下:
public
void
BatchSerialize(
string
filename, Voucher[] vouchers)
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream fs = new FileStream(filename, FileMode.Create);
foreach (Voucher voucher in vouchers)
{
formatter.Serialize(fs, voucher);
}
fs.Close();
}
public Voucher[] BatchDeserialize( string filename)
{
BinaryFormatter formatter = new BinaryFormatter();
ArrayList vouchers = new ArrayList();
FileStream fs = new FileStream(filename, FileMode.Open);
try
{
while (fs.Position != fs.Length)
{
vouchers.Add(formatter.Deserialize(fs));
}
}
finally
{
fs.Close();
}
return (Voucher[])vouchers.ToArray(typeof(Voucher));
}
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream fs = new FileStream(filename, FileMode.Create);
foreach (Voucher voucher in vouchers)
{
formatter.Serialize(fs, voucher);
}
fs.Close();
}
public Voucher[] BatchDeserialize( string filename)
{
BinaryFormatter formatter = new BinaryFormatter();
ArrayList vouchers = new ArrayList();
FileStream fs = new FileStream(filename, FileMode.Open);
try
{
while (fs.Position != fs.Length)
{
vouchers.Add(formatter.Deserialize(fs));
}
}
finally
{
fs.Close();
}
return (Voucher[])vouchers.ToArray(typeof(Voucher));
}
本文的例子使用的是FileStream,因此在Deserialize的代码中使用 fs.Position != fs.Length代表是否已读到文件尾,但在其他的Stream中,比如NetworkStream,这一逻辑需要适当修改。
NUnit对应的测试代码如下:
[Test]
public void TestSerializeVouchers()
{
Voucher[] vouchers = new Voucher[] {new Voucher("2005012900001", "xingd", DateTime.Now), new Voucher("2005012900001", "xingd", DateTime.Now)};
VoucherSerializer serializer = new VoucherSerializer();
serializer.BatchSerialize("voucher.dat", vouchers);
}
[Test]
public void TestDeserializeVouchers()
{
VoucherSerializer serializer = new VoucherSerializer();
Voucher[] vouchers = serializer.BatchDeserialize("voucher.dat");
Assert.AreEqual(vouchers.Length, 2);
Assert.AreEqual(vouchers[0].VoucherId, "2005012900001");
Assert.AreEqual(vouchers[0].Creator, "xingd");
Assert.AreEqual(vouchers[1].VoucherId, "2005012900001");
Assert.AreEqual(vouchers[1].Creator, "xingd");
}
public void TestSerializeVouchers()
{
Voucher[] vouchers = new Voucher[] {new Voucher("2005012900001", "xingd", DateTime.Now), new Voucher("2005012900001", "xingd", DateTime.Now)};
VoucherSerializer serializer = new VoucherSerializer();
serializer.BatchSerialize("voucher.dat", vouchers);
}
[Test]
public void TestDeserializeVouchers()
{
VoucherSerializer serializer = new VoucherSerializer();
Voucher[] vouchers = serializer.BatchDeserialize("voucher.dat");
Assert.AreEqual(vouchers.Length, 2);
Assert.AreEqual(vouchers[0].VoucherId, "2005012900001");
Assert.AreEqual(vouchers[0].Creator, "xingd");
Assert.AreEqual(vouchers[1].VoucherId, "2005012900001");
Assert.AreEqual(vouchers[1].Creator, "xingd");
}
TestSerializeVouchers中使用了两个相同的Voucher对象测试,这样做的目的是想分析生成的voucher.data文件的内容:
整个文件的长度为0x01AA字节,从前半部分与后半部分(0x00000000与0x000000D5)的对比可以看出,序列化到同一个流中的多个对象是按序存储的,相互之间不会有任何影响。
在C#中,一切都是对象,所有的类都从System.Object派生。相对的,一切数组实例也都是对象,所有的数组类都在System.Array派生。而Array类是Serializable的,那么Voucher[]是否能够直接进行序列化呢,我们可以尝试另外一种序列化多个Voucher的方式,代码如下:
public
void
BatchSerialize(
string
filename, Voucher[] vouchers)
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream fs = new FileStream(filename, FileMode.Create);
formatter.Serialize(fs, vouchers);
fs.Close();
}
public Voucher[] BatchDeserialize( string filename)
{
BinaryFormatter formatter = new BinaryFormatter();
Voucher[] vouchers = null;
FileStream fs = new FileStream(filename, FileMode.Open);
try
{
vouchers = (Voucher[])formatter.Deserialize(fs);
}
finally
{
fs.Close();
}
return vouchers;
}
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream fs = new FileStream(filename, FileMode.Create);
formatter.Serialize(fs, vouchers);
fs.Close();
}
public Voucher[] BatchDeserialize( string filename)
{
BinaryFormatter formatter = new BinaryFormatter();
Voucher[] vouchers = null;
FileStream fs = new FileStream(filename, FileMode.Open);
try
{
vouchers = (Voucher[])formatter.Deserialize(fs);
}
finally
{
fs.Close();
}
return vouchers;
}
因为接口没有变化,因此可以用之前的NUnit测试方法,测试通过。因此,可以得出结论, 如果类A能够被序列化,那么A[]也能被序列化。
NUnit测试方法序列化Voucher[]时生成的二进制文件内容如下:
为了比较两种序列化的方式,把多个对象依次序列化产生的文件内容列在下面:
采用序列化数组的方法产生的文件要比将多个对象依次序列化产生的文件要小得多。数组序列化时,Assembly的信息,Voucher class的信息,Voucher fields的信息都只需要一次输出,而将多个对象依次序列化时,相应的信息要为每一个对象输出。
除了类型信息之外,还有一个有意思的现象,"2005012900001"和"xingd"在数组序列化时只输出了一次,但NUnit的测试中是产生了两个Voucher对象的,虽然它们的voucherId和creator的值是一样的。.是不是因为值一样,所以才只输出一次呢。将测试方法修改如下:
[Test]
public void TestSerializeVouchers()
{
Voucher[] vouchers = new Voucher[] {new Voucher("2005012900001", "xingd", DateTime.Now), new Voucher("2005012900002", "steven", DateTime.Now)};
VoucherSerializer serializer = new VoucherSerializer();
serializer.BatchSerialize("voucher.dat", vouchers);
}
public void TestSerializeVouchers()
{
Voucher[] vouchers = new Voucher[] {new Voucher("2005012900001", "xingd", DateTime.Now), new Voucher("2005012900002", "steven", DateTime.Now)};
VoucherSerializer serializer = new VoucherSerializer();
serializer.BatchSerialize("voucher.dat", vouchers);
}
输出的vouhcer.dat内容如下:
可以看到,两个Voucher对象的voucherId和creator都有输出了。但是,在这两种测试方法中,两个Voucher对象的createdTime都是分别输出的,但实际上它们应该具有相同的值。那么,是不是因为DateTime是值类型,而string是引用类型。NET中对字符串处理时,会产生一个内置的字符串池,具有相同值的字符串会引用值中的同一个对象。为了进一步了解,再对测试方法进行修改:
[Test]
public void TestSerializeVouchers()
{
Voucher voucher = new Voucher("2005012900001", "xingd", DateTime.Now);
Voucher[] vouchers = new Voucher[] {voucher, voucher};
VoucherSerializer serializer = new VoucherSerializer();
serializer.BatchSerialize("voucher.dat", vouchers);
}
public void TestSerializeVouchers()
{
Voucher voucher = new Voucher("2005012900001", "xingd", DateTime.Now);
Voucher[] vouchers = new Voucher[] {voucher, voucher};
VoucherSerializer serializer = new VoucherSerializer();
serializer.BatchSerialize("voucher.dat", vouchers);
}
生成的文件内容如下:
明显的可以看出,只有一个Voucher对象产生了输出。 在BinaryFormatter序列化过程中,似乎是首先生成要序列化对象的整个Graph,然后将涉及的对象一一序列化的。因此,不仅是数组中相同的对象避免重复输出,在序列化对象所引用的对象形成的Graph中,相同的对象只会输出一次。
通过本文的比较可以确定,应当 优先考虑将集合类整体序列化,而不要自己将集合中的每一个元素依次序列化