65.9K
CodeProject 正在变化。 阅读更多。
Home

CompactFormatterPlus:适用于 Full 和 Compact Framework 的通用序列化器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (17投票s)

2007 年 12 月 1 日

CPOL

9分钟阅读

viewsIcon

117269

downloadIcon

2336

适用于 Full 和 Compact Framework 的通用序列化器

引言

.NET Framework 的出现是过去 10 年编程界最重要的事件,而且我认为它仍然被低估了。它是迄今为止最稳定、最全面的框架。然而,没有人和事物是完美的。本文将讨论 .NET Framework 通信,特别是紧凑框架通信(序列化)。

通信系统模型

什么是通信框架?通常,通信框架是一个对象传递系统。仅此而已。如果该系统实现了 RPC/RMI,则会在其之上构建另一个层。对象传递系统反过来又只包含两个子层:

  1. 传输层(物理上将字节流传输到目的地) 
  2. 从字节流到对象的转换器(序列化器)仅此而已。

其余的都可以隐藏在这个理想的通信框架中。
最后一句仅适用于以相同术语描述对象的系统(相同的操作系统、相同的 .NET Framework)。

最小(最佳)通信系统

最小通信系统只有一个方法 Send(Object)。程序员是否需要关心对象如何转换为字节流,如何由线程管理,如何排队、分片等?不,这是通信框架的职责。

WCF

当我第一次看到 WCF 时,我感觉我错过了什么。这不可能是真的:自动通用序列化消失了!取而代之的是一种半手动、非常受限的序列化。您自己判断:

public class Person 
{ 
    public string FirstName; 
    public string LastName; 
    public int ID; 
    ArrayList alist = new ArrayList(); 
    public ClassB _b; 
} 

在 WCF 中,这样一个原始对象的序列化装饰如下:

[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")] 
class Person : IExtensibleDataObject 
{ 
    [DataMember()] 
    public string FirstName; 
    [DataMember] 
    public string LastName;     
    [DataMember()] public int ID; 
    [DataMember()] 
    ArrayList alist = new ArrayList(); 
    [DataMember()] public ClassB _b; 
    private ExtensionDataObject extensionData_Value; 
    public ExtensionDataObject ExtensionData 
    { 
        get 
        {     
            return extensionData_Value; 
        } 
        set 
        { 
            extensionData_Value = value; 
        }
     } 
} 

大家都喜欢这样吗?显然不是。网络上充斥着博客,建议了许多“聪明”的解决方案来克服 WCF 的限制性。有限的 WCF 序列化被“成功”地通过使用 BinaryFormatter 然后将字节数组作为参数来规避。这是互操作性付出的代价吗?微软显然牺牲了通用性以换取互操作性,仅仅是通过限制功能。这种方法的逻辑极限将是回归到老式套接字通信。因此,大多数 WCF 程序都在 Windows 上运行,拥有通用序列化器作为默认设置,而其余作为选项是否更明智?

什么使通信框架特定于操作系统?

使系统特定于操作系统的唯一因素是格式化器(序列化器)。它是系统中将流转换为对象的唯一组件。流总是相同的。正如雕塑由石头(任何石头)制成一样,对象由流制成。事实上,WCF 拥有所有这些零碎的东西,但系统的组合方式远非完美。

互操作性

正如我之前提到的,序列化器使框架特定于操作系统。反过来,序列化器存在于框架中 [例如 .NET]。如果操作系统不支持框架,甚至没有序列化器的概念怎么办?在这种特定情况下,手动(或半手动)处理字节流是合适的,但这应该是可选的,而不是默认的。

如果你喜欢一切自动化,为什么不使用 Remoting?

标准 Remoting 基于简单的同步通信模型。它是单向的。尝试使用回调进行双工通信在客户端位于代理或 NAT 后面时不起作用,因为 Remoting 会为事件打开第二个连接。由于防火墙是标准的网络组件,我们可以忘记使用 Remoting 进行双向通信。

全部回忆

.NET 标准通信遭受着与 10 年前的 Windows OS 相同的疾病。核心 Windows API 与 10 年前相同,但 10 年前编写 Windows Form 应用程序是一件非常困难的事情。为什么?因为程序员必须手动提供所有参数。为了提供所有参数,他需要了解所有的 Windows 内部机制以及如何使用它们。在 .NET 环境中,所有默认设置都由框架设置。我们从工具箱拖动控件,其余的会自动完成。如果我们想要额外功能,我们可以从属性网格中选择它们或在代码中键入它们。

在现实生活中,我们将对象(信件或包裹)发送给收件人。我们真的关心信件是如何递送的,邮递员的名字或他宠物的名字是什么吗?不,我们不关心。通常我们把信放进邮箱,其余的就由系统默认完成。如果我们想要一些额外功能,例如更好的安全性或送达确认,我们可以额外获得。如果信件递送看起来像 .NET 通信(尤其是 WCF),那么信件递送过程肯定会是这样的:我们从目录中选择运送信件的汽车的车牌号、司机的名字、航班号、飞机的类型、信封的颜色、品牌……。很可能这会阻止我们发送信件。

替代方案

当 IT 行业的高僧们说一切都比以往更好时,通信框架(主要是基于 .NET 套接字的)却不断增多。
The Code Project 网站至少托管了 10 个这样的框架。设计一个功能有限的原始通信类(通常已经足够了)并不难,但主要的障碍是序列化器,特别是对于紧凑框架。

紧凑框架 Remoting

不幸的是,Remoting 和 Binary formatter 没有(而且似乎也不会)为紧凑框架实现。唯一的选择是使用有限的 XMLSerializer (它不是通用的),并且无法在技术上实现您可以在一端简单地放入对象,在另一端取出的系统。除了序列化器,其他组件如通道或线程在 CF 中都很容易获得。

紧凑框架序列化

CompactFormatterPlus:用于紧凑框架的通用二进制序列化器

这项工作基于 Angelo Scotto 精彩的 CompactFormatter,适用于完整框架和紧凑框架。原始的 CompactFormatter 是为 .NET 1.1 编写的。
然而,岁月流逝,改进的机会也随之出现。重新设计的根本原因如下:

  1. 紧凑框架 2.0 具有可序列化属性。此属性仅为与完整框架兼容而引入。除此之外,此属性的实际价值是可疑的,因为在此属性引入之前构建的类显然没有它。仅仅依赖此属性会使完全可序列化的类变得不可序列化。原始的 CompactFormatter 具有类似的属性,但它与完整框架不兼容。检查此属性(Angelo 的可序列化属性)已被移除。这使得为完整框架和紧凑框架创建的类兼容。尽管存在一些缺点——完全不可序列化的对象将被尝试序列化。
  2. CompactFormatter DataSet DataTable 的序列化不完整。SimmoTech 设计的 DataSet 序列化要先进得多。Simotech 序列化和代码可以在 The Code Project 文章中找到。这段代码被用作代理整合到 CompactFormatter 中。
  3. 为了性能原因,原始 CompactFormatter 将类的索引放入流中,而不是类名。如果序列化器在通信会话期间一致存在,这很好。实际上并非总是如此。序列化器可能会动态实例化,而 CompactFormatter 肯定会失败。
  4. 反序列化时实例化类的方式:CompactFormatter 使用不完整的程序集名称或完全限定的程序集名称。如果通信平台不同,这两种方法都会失败。例如,当对象在完整框架上序列化而在紧凑框架上反序列化时,完全限定的程序集名称没有意义。只有原始类型和序列化器内部定义的类型才能工作。
  5. 现在可以使用自动代理生成工具来生成代理。

极致性能和序列化工作室

原始 CompactFormatter 使用代理、重写和自定义序列化来实现更好的性能。这当然是有效的;然而,即使对经验丰富的程序员来说,编写代理也总是一项棘手且繁琐的工作。为了使这项任务更容易,移除了自定义序列化和代理。

现在只有一个自定义序列化——重写。

重写(根据 Angelo Scotto 的说法)是允许为特定类注入自定义序列化模块的原始机制。此外,CompactFormatter 已经被重新设计为包含自动生成的二进制序列化器。序列化工作室是生成超快二进制序列化器(比 BinaryFormatter 快 10-60 倍)的工具。
该工具是免费的,可以从 这里下载。该工具专门针对 CompactFormatterPlus 生成代理进行了优化。

将快速序列化器添加到 CompactFormatterPlus

CompactFormatterPlus cfp = new CompactFormatterPlus(); 
MyFastSerializer mfs = new MyFastSerializer(); 
cfp.addFastSerializer(mfs); 

如果快速序列化器无法序列化对象怎么办?这种情况的例子可以用下面的代码来说明:

public class MyClass1 
{ 
    public int SomeInteger; 
    public object UnknownObject; 
} 

如果为该类生成了快速序列化器,则必须在运行时序列化 UnknownObject ,因为它的类型在编译时未知。如果发生这种情况,通用序列化器将接管控制并继续序列化。反过来,如果 CompactFormatterPlus 检测到可以被快速序列化器序列化的字段(类),则控制将递归地返回到快速序列化器。实际上,程序员不必了解序列化的这些复杂性。快速序列化器(如果有)只需添加到 CompactFormatterPlus 即可。快速序列化器的运行时由 FSWriter FSReader 组成。与原始的 Binary reader 和 writer 相比,它有一些额外的方法。它看起来有点丑陋,因为而不是继承 Binary reader(writer)的所有内容,它有自己的所有重载实现。原因是:CF 编译器似乎有一个 bug。重载的 Write (byte) 被评估为 [可能是] Write(double)。[8 字节而不是 1]

Using the Code

在 PC 端,需要序列化的类应作为源代码或库包含在解决方案中。对于库,它必须在 PC 项目中引用。您的类必须具有与 PDA 上的命名空间和类名相同的命名空间名称和类名。以上所有内容均适用于 PDA。
解决方案是独立的。PC 文件不应在 PDA 端使用或引用,反之亦然。通用的是名称。

有时可以在 PC 上使用 CF 文件(EXE 和 DLL),在 PDA 上使用 PC 文件。但这是一种不良做法。它可能会起作用,也可能会出现奇怪且不相关的消息而失败。
典型场景:在调试会话期间,如果在移动解决方案中有 PC 生成的程序集,该文件将把所有引用的程序集拖到 PDA 上,您将耗尽内存。

CompactFormatterPlus 可序列化类型

  • 所有原始类型
  • 原始类型数组
  • 日期时间
  • ArrayList
  • 哈希表
  • List<T>
  • Dictionary<T Key, T1 Value>
  • DataSet
  • DataTable
  • 由以上类型组成的复杂对象

致谢

用户 Fabien Castell 改进了类型数据集的序列化以及对象的实例化,如果它们定义在不同的程序集中。代码可以从原始网站下载。

新版本

如果发布了新版本,可以从 这里 下载。

© . All rights reserved.