常见问题 开发软件的常见问题>>

我有一个含有许多控件的智能设备窗体。为什么会在运行时得到“NotSupportedException”?

发布时间:2010-11-23 15:55:16

答案:

如果您有一个含有许多控件的窗体,当您运行应用程序时可能会得到 NotSupportedException。如果您在调试器下运行,则可能会发现异常是由调用窗体构造函数中的 InitializeComponent() 引发的。您最可能遇到的一个事实是 Compact Framework CLR 硬性限制每个方法只有 64KB JITed 代码。这意味着当 CLR 将 IL 转换为方法(在本例中为 InitializeComponent 方法)时,产生的本机代码必须少于 64KB。如果产生的本机代码大于此值,则会引发 NotSupportedException。也可能出现的情况是,当在调试器下运行(例如通过 F5)时会看到异常,但不带调试器运行(通过 Ctrl-F5)时不会有异常。当 JITed 代码的大小非常接近于 64KB 限制时就会出现这种情况,因为调试器在运行时需要生成其他一些代码。

也可能出现的情况是,在设备中会产生这种异常,但在模拟器中不会(或反之),因为处理器家族之间 JITed 代码的大小有所不同(例如,许多基于 Windows Mobile 的 Pocket PC 设备使用 ARM 指令,而模拟器使用 x86 指令)。

超出此限制之前,窗体中可含有的控件数并没有一个固定的值。这是因为设计器生成的代码量因控件而异。例如,Button 控件通常生成的代码少于 TabControl 生成的代码,这取决于控件属性的设置方式。如果您通过属性网格中的生成器为控件填充集合,则拥有集合的控件(比如 ListBox 或 TreeView 控件)会生成大量代码。另外,本地化窗体(其 Localizable 属性设置为 True)中控件生成的代码会比非本地化窗体多,因为 InitializeComponent 中生成的代码会根据来自资源文件的值设置属性,而不是使用硬编码的值。

如果您碰到这种问题,有几种不同的技术可以使用:

将一个窗体分割成多个窗体。将大量控件放在一个窗体中对窗体加载和应用程序启动时间也有不利的影响。如果可能,要将 UI 分在两个或更多的窗体中。

不要通过设计时生成器填充大的内部控件集合。例如,如果您在 TreeView 控件的 Nodes 集合中添加许多节点,就会在 InitializeComponent 方法中额外添加大量代码。如果可能,应该移动这些代码,使集合填充到构造函数或 Form.Load 事件处理程序中。这种技术的缺点是集合将无法很容易地通过设计器编辑,但有助于调整生成代码的大小。

不要将自己的代码添加到 InitializeComponent 方法中。这通常是一个很好的指导原则,因为在这个方法中修改或添加由设计器生成的代码是不受支持的。这样做也会导致设计器中出现意外行为。如果您要将自己的自定义启动代码添加到窗体中,就应该将它放在构造函数或 Form.Load 事件处理程序中。

在运行时以编程方式初始化类似的控件。例如,如果您创建了 12 个 Button 控件,它们只有 Text 和 Position 属性不同,则您可以考虑通过一个循环来创建和初始化,而不是让这 12 个控件都在设计器生成的代码中创建和初始化。另外,如果您自己编写代码实现,不要将代码放在 InitializeComponent 中。

修改 InitializeComponent 的一个令人遗憾的负面影响是:在 InitializeComponent 外的代码中实例化的任何控件都无法使用窗体设计器编辑。另外,如果您手动编辑 InitializeComponent 中的代码,则会发现设计器可能无法正确解释您修改过的代码,从而不再呈现窗体。基于这些原因,最好选择上面列出的不修改 InitializeComponent 的解决办法。

本FAQ适用范围

下一步您可以:
查看开发软件产品 >>