BinaryFormatter序列化实例(四)

将Voucher[] vouchers进行序列化时,可以看到Assembly和Type的信息只生成了一次,但是如果在vouchers中包含派生自Voucher的类的实例,也就是说vouchers是一个多态数组的时候,又是什么情况呢。如果BinnaryFormatter保存的是静态类型,那么反序列化时就不能完整恢复对象了。另一方面,因为BinaryFormatter.Serialize方法的参数是object类型,BinaryFormatter应当是保存了对象的动态类型的。为验证这一猜想,从Voucher类派生出BillVoucher。 

  BillVoucher代表从单据生成的凭证,它需要追踪原始的单据。BillVoucher类的定义如下:
namespace  GeneralLedger 
{ 
    
public class BillVoucher : Voucher 
    
{ 
        
private string billId; 
         
        
public BillVoucher(string billId, string voucherId, string creator, DateTime createTime) : base(voucherId, creator, createTime) 
        
{ 
            
this.billId = billId; 
        }
 
 
        
public string BillId 
        
{ 
            
get 
            
{ 
                
return billId; 
            }
 
        }
 
    }
 
}

  注意到BillVoucher并没有添加SerializableAttribute,这是因为一些Attribute是可以从基类继承的。那么SerializableAttribute能不能被继承呢,使用NUnit添加测试方法:  

[Test] 
public   void  TestSerializeBillVoucher() 
{ 
    BillVoucher voucher 
= new BillVoucher("2005020400001""2005012900001""xingd", DateTime.Now); 
    VoucherSerializer serializer 
= new VoucherSerializer(); 
    serializer.Serialize(
"voucher.dat", voucher); 
}

  运行NUnit进行测试,报错。会不是因为派生类多了一个字段呢?将billId暂时注释,运行测试报同样错误。由此可知, 支持BinaryFormatter序列化的类必须添加SerializableAttribute,即便是其基类也支持BinaryFormatter序列化。为BillVoucher添加SerializableAttribute后,测试通过。  

  如果基类不支持序列化,而派生类添加了SerializableAttribute,又会发生什么样的情况呢?从实际应用来说,应该是不允许的,否则也就意味着任何可以派生的类都可以间接的实现序列化了。为了近一步验证,暂时将Voucher类的SerializableAttribute注释掉。运行NUnit测试TestSerializeBillVoucher,出错,提示如下:  

GeneralLedger.VoucherTest.TestSerializeBillVoucher : System.Runtime.Serialization.SerializationException : The type GeneralLedger.Voucher in Assembly Voucher, Version=1.0.1861.37679, Culture=neutral, PublicKeyToken=null is not marked as serializable. 

  出错信息明确的说明了类Voucher没有标记为Serializable。  

  将Voucher的SerializableAttribute加上,接下来,我们开始测试多态数组的情况,接TestSerializeVouchers和TestDeserializeVouchers修改如下:  
  
[Test] 
public   void  TestSerializeVouchers() 
{ 
    Voucher[] vouchers 
= new Voucher[] {new Voucher("2005012900001""xingd", DateTime.Now), new BillVoucher("2005020400001""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].GetType(), typeof(BillVoucher)); 
    Assert.AreEqual(((BillVoucher)vouchers[
1]).BillId, "2005020400001"); 
    Assert.AreEqual(vouchers[
1].VoucherId, "2005012900001"); 
    Assert.AreEqual(vouchers[
1].Creator, "xingd"); 
}
 


  运行后,两个测试都通过。生成的voucher.dat内容如下:

   

  由voucher.data的内容,我们可以得出以下结论: 

  1. BinaryFormatter序列化基于对象的动态类型。
  2. 派生类序列化时,基类的字段以base+field的方式作为标识。
  3. 对某一个object进行BinaryFormatter序列化时,由object所引用的对象所形成的Graph里,相同的Assembly只生成一份包含Assembly信息的输出,相同的Type也只生成一份包含类型信息的输出。

  有意思的是,在00000060那一行,有一个看似多余的GeneralLedger.Voucher的字符串,猜测是用来记录vouchers的静态类型信息,也就是Voucher[]。Voucher[]的类型信息可能由Voucher的类型信息和一个数组的标识组成。在后面我们对序列化实现过程的生成文件格式分析的时候,会对这一猜测进行验证。有兴趣的朋友也可以自己验证一下。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值