为什么Visual Studio IDE有时会初始化"this.components"对象,而有时不会?

15

最近,我注意到 Visual Studio Designer (C#) 的某些行为,这些行为让我感到困惑,想知道是否有人能够澄清一下...

在我的某些 Windows Forms 上,设计器生成的代码的第一行是:

this.components = new System.ComponentModel.Container();

当出现这种情况时,在同一个设计文件中的 dispose 方法会在以下方式下将两个“Dispose”调用放置在 if 条件语句内:

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
            base.Dispose(disposing);
        }
    }

也就是说,只有在disposing为true而且components不为null的情况下才会调用任何内容。

在某些其他窗体中,设计器生成的代码中缺少第一行。在这些情况下,base.Dispose调用位于“if”条件之外,如下所示...

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

在追踪一个表单不关闭的错误时,我注意到这一点,其中this.components为null,但是base.Dispose调用在该条件内部(我怀疑设计师代码已被篡改,但那是另一个故事。

是什么控制这种行为?

(项目中早期的一些窗体是在VS 2005中创建的,现在我们使用的是VS 2008 - 这是一个线索吗?)

4个回答

6
这是可以重现的行为。创建新表单时,它将以包括 this.components 构造函数调用的骨架开始。然后添加组件(例如 Timer)并再次删除它后,设计器会重新生成代码,此时不再包含构造函数调用。这不是错误。
顺便说一下,骨架代码是由 Common7\IDE\ItemTemplates\CSharp\Windows Forms\1033\Form.zip\form.designer.cs 生成的。
在 if() 语句中看到 base.Dispose() 调用是个错误。那可能是自己引起的。或者可能是骨架代码的 beta 版本。VS2005 没问题。请检查 ItemsTemplatesCache 文件夹。

谢谢nobugz。我现在也有机会在VS2005中尝试这种情况了。没问题。正如你所说,我只能假设这是“自己造成的”。 - Stuart Helwig
现在没有构造函数调用了。 (components!= null)检查的意义是什么,如果根本没有components初始化?谁、何时和何地初始化components?顺便说一下,我从VS2010、.NET4.0来到这里。 - Fulproof
您似乎有一个新问题。您可以通过点击“提问”按钮来提出它。 - Hans Passant
@HansPassant 那么我们不应该担心它,因为GC会释放例如Timer的内存空间? - Thomas

4
六年过去了,这个问题仍然存在。我已经成功地找到了它发生的至少一个原因。
当测试你的组件是否有一个接受IContainer参数的构造函数时,System.ComponentModel.Design.Serialization.ComponentCodeDomSerializer会缓存你项目中的IContainer类型引用。如果你保存了另一个项目中的对象,或者在你的项目中进行了其他类型的更改,ComponentCodeDomSerializer将无法找到构造函数,因为IContainer类型不再等于它缓存的类型。
如果这种情况在你的项目中频繁发生,有一个非常丑陋的解决办法。把这个 VB 或者 C#VisualStudioWorkaroundSerializer 类添加到你的解决方案中。然后在你的组件上添加属性 DesignerSerializer(GetType(VisualStudioWorkaroundSerializer), GetType(CodeDomSerializer)) 。每当你的组件被保存时,这个自定义序列化程序将检测到问题,修复它,并在将要发生此问题时强制你重新保存。

1

有趣的故障!它确实听起来像是设计师/模板中一个版本的错误。当然,如果您认为设计师代码已被篡改,那么所有的赌注都基本上无效了...

然而,在VS2008中,它生成了毫无疑问正确的版本:

if (disposing && (components != null))
{
    components.Dispose();
}
base.Dispose(disposing);

所以基本的Dispose(...)被调用了。不幸的是,我手头没有VS2005来进行测试。但是-它不会在必要时初始化组件-声明如下::

private System.ComponentModel.IContainer components = null;

然后,如果需要的话,在InitializeComponent中填充它:

private void InitializeComponent()
{
    this.components = new System.ComponentModel.Container();
    //...
}

我猜想使用这个结构,它只需要维护InitializeComponent(而不是字段本身)。


我也不行,马克。我今晚得回家试试了。谢谢。 - Stuart Helwig
如果您认为设计师的代码已被篡改 - 那是因为我不知道如何篡改它! - Fulproof

0

我曾经见过这种情况发生,有时候Dispose方法会警告组件从未被赋值或未定义。

我认为这是两个因素的结合:

  1. Visual Studio版本之间代码生成略有不同
  2. 如果文件中已经存在Dispose方法,则不会生成Dispose方法,而每次都会生成InitializeComponent(和相关声明)

这导致了一个InitializeComponent/声明部分与Dispose方法不协调的结果。


"在Visual Studio的不同版本之间" - 我是从在VS2010中创建全新的Windows Forms应用程序开始的,.NET4.0,而没有使用任何其他版本(尽管我在PC上安装了VS2008)。 - Fulproof

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接