xingd.net

.net related techonology

导航

BinaryFormatter序列化实例(五)

Posted on 2005-02-04 21:38  xingd  阅读(1361)  评论(0编辑  收藏  举报

  将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的类型信息和一个数组的标识组成。在后面我们对序列化实现过程的生成文件格式分析的时候,会对这一猜测进行验证。有兴趣的朋友也可以自己验证一下。