为什么Visual Studio IDE有时会初始化“this.components对象:有时候不会?

时间:2022-03-01 15:42:45

I've recently noticed some behaviour with the Visual Studio Designer (C#) that I don't understand and was wondering if someone could clarify...

我最近注意到一些我不理解的Visual Studio Designer(C#)的行为,并且想知道是否有人可以澄清......

One some of my Windows Forms, the first line of the designer generated code reads;

我的一些Windows窗体,设计器的第一行生成代码读取;

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

When this is the case, the dispose method, in that same designer file, the dispose method places two "Dispose" calls within the case "if" condition as follows;

在这种情况下,dispose方法,在同一设计器文件中,dispose方法在case“if”条件下放置两个“Dispose”调用,如下所示;

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

i.e. Nothing is called unless disposing is true, AND components is not null.

即,除非disposing为true,否则调用Nothing,AND组件不为null。

On some other forms, that first line in the designer generated code is missing. In these cases the base.Dispose call is outside the "if" condition as such...

在其他一些表单中,缺少设计器生成代码中的第一行。在这些情况下,base.Dispose调用在“if”条件之外......

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

I have noticed this while tracking down a bug with a form not closing, where this.components was null, yet the base.Dispose call was inside that condition (I suspect the designer code had been tampered with but that's another story.

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

What controls this behaviour?

什么控制了这种行为?

(Some earlier forms in the project were created in VS 2005 and we now use VS 2008 - clue?)

(项目中的一些早期形式是在VS 2005中创建的,我们现在使用VS 2008 - 线索?)

4 个解决方案

#1


This is reproducible behavior. When you create a new form, it starts out with a skeleton that includes the this.components constructor call. When you then add a component (say a Timer) and remove it again, the designer regenerates the code, now without the constructor call. That isn't a bug.

这是可重现的行为。创建新表单时,它首先使用包含this.components构造函数调用的框架。然后,当您添加一个组件(比如一个Timer)并再次删除它时,设计器会重新生成代码,现在没有构造函数调用。那不是错误。

Fwiw, the skeleton code is generated by Common7\IDE\ItemTemplates\CSharp\Windows Forms\1033\Form.zip\form.designer.cs

Fwiw,骨架代码由Common7 \ IDE \ ItemTemplates \ CSharp \ Windows Forms \ 1033 \ Form.zip \ form.designer.cs生成

Seeing the base.Dispose() call inside the if() statement is a bug. That might be self-induced. Or it might be a beta version of the skeleton code. VS2005 does it right. Do check the ItemsTemplatesCache folder.

在if()语句中查看base.Dispose()调用是一个错误。这可能是自我诱导的。或者它可能是骨架代码的beta版本。 VS2005做对了。检查ItemsTemplatesCache文件夹。

#2


6 years later and this problem still occurs. I've managed to track down at least one cause for it happening.

6年后,这个问题仍然存在。我已经设法找到了至少一个导致它发生的原因。

When testing if your component has a constructor that takes an IContainer, System.ComponentModel.Design.Serialization.ComponentCodeDomSerializer caches a reference to the Type of IContainer for your project. If you then save an object for another project within the same solution, or perhaps when you have made some other types of changes in your project, ComponentCodeDomSerializer can no longer find the constructor as the Type of IContainer is no longer equal to it's cached Type.

在测试组件是否具有使用IContainer的构造函数时,System.ComponentModel.Design.Serialization.ComponentCodeDomSerializer会缓存对项目的IContainer类型的引用。如果然后在同一解决方案中为另一个项目保存对象,或者当您在项目中进行了其他类型的更改时,ComponentCodeDomSerializer将无法再找到构造函数,因为IContainer的类型不再等于它的缓存类型。

If this is happening lots for your project, there is a very ugly workaround. Add this VB or C# VisualStudioWorkaroundSerializer class to your solution. Then add the attribute DesignerSerializer(GetType(VisualStudioWorkaroundSerializer), GetType(CodeDomSerializer)) to your component. Whenever your component is saved, this custom serializer will detect the problem, fix it, and force you to save again whenever this issue is about to occur.

如果您的项目发生了很多,那么有一个非常难看的解决方法。将此VB或C#VisualStudioWorkaroundSerializer类添加到您的解决方案中。然后将属性DesignerSerializer(GetType(VisualStudioWorkaroundSerializer),GetType(CodeDomSerializer))添加到组件。每当保存组件时,此自定义序列化程序将检测到问题,修复它,并强制您在此问题即将发生时再次保存。

#3


Interesting glitch! It does indeed sound like a bug in one version of the designer / templating. Of course, if you think the designer code had been tampered, all bets are pretty-much off anyway...

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

However, in VS2008, it generates the undoubtably correct version:

但是,在VS2008中,它会生成无可置疑的正确版本:

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

So the base Dispose(...) is called. I haven't got VS2005 handy to test it, unfortunately. However - it doesn't initialize the components until it has to - the declaration is:

所以调用了基础Dispose(...)。不幸的是,我还没有得到VS2005进行测试。但是 - 它不会初始化组件,直到它必须 - 声明是:

private System.ComponentModel.IContainer components = null;

And then if it is needed, it is populated in InitializeComponent:

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

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

I guess with this construct it only has to maintain InitializeComponent (and not the fields itself).

我猜这个构造只需要维护InitializeComponent(而不是字段本身)。

#4


I have seen this happen, and I've also occasionally got warnings about from the Dispose method about components either never having its value assigned, or not being defined.

我已经看到这种情况发生了,而且我偶尔也会从Dispose方法得到有关组件的警告,这些组件要么从未分配其值,要么未定义。

I think it is a combination of two things:

我认为这是两件事的组合:

  1. Slightly different code generation between versions of Visual Studio
  2. Visual Studio版本之间的代码生成略有不同

  3. The Dispose method is only generated if there is not one already in the file, whereas InitializeComponent (and associated declarations) is generated each time
  4. 只有在文件中没有已经存在的情况下才会生成Dispose方法,而每次都会生成InitializeComponent(和关联的声明)

This results in an InitializeComponent/declarations section that is out-of-whack with the Dispose method.

这导致InitializeComponent / declarations部分与Dispose方法不同。

#1


This is reproducible behavior. When you create a new form, it starts out with a skeleton that includes the this.components constructor call. When you then add a component (say a Timer) and remove it again, the designer regenerates the code, now without the constructor call. That isn't a bug.

这是可重现的行为。创建新表单时,它首先使用包含this.components构造函数调用的框架。然后,当您添加一个组件(比如一个Timer)并再次删除它时,设计器会重新生成代码,现在没有构造函数调用。那不是错误。

Fwiw, the skeleton code is generated by Common7\IDE\ItemTemplates\CSharp\Windows Forms\1033\Form.zip\form.designer.cs

Fwiw,骨架代码由Common7 \ IDE \ ItemTemplates \ CSharp \ Windows Forms \ 1033 \ Form.zip \ form.designer.cs生成

Seeing the base.Dispose() call inside the if() statement is a bug. That might be self-induced. Or it might be a beta version of the skeleton code. VS2005 does it right. Do check the ItemsTemplatesCache folder.

在if()语句中查看base.Dispose()调用是一个错误。这可能是自我诱导的。或者它可能是骨架代码的beta版本。 VS2005做对了。检查ItemsTemplatesCache文件夹。

#2


6 years later and this problem still occurs. I've managed to track down at least one cause for it happening.

6年后,这个问题仍然存在。我已经设法找到了至少一个导致它发生的原因。

When testing if your component has a constructor that takes an IContainer, System.ComponentModel.Design.Serialization.ComponentCodeDomSerializer caches a reference to the Type of IContainer for your project. If you then save an object for another project within the same solution, or perhaps when you have made some other types of changes in your project, ComponentCodeDomSerializer can no longer find the constructor as the Type of IContainer is no longer equal to it's cached Type.

在测试组件是否具有使用IContainer的构造函数时,System.ComponentModel.Design.Serialization.ComponentCodeDomSerializer会缓存对项目的IContainer类型的引用。如果然后在同一解决方案中为另一个项目保存对象,或者当您在项目中进行了其他类型的更改时,ComponentCodeDomSerializer将无法再找到构造函数,因为IContainer的类型不再等于它的缓存类型。

If this is happening lots for your project, there is a very ugly workaround. Add this VB or C# VisualStudioWorkaroundSerializer class to your solution. Then add the attribute DesignerSerializer(GetType(VisualStudioWorkaroundSerializer), GetType(CodeDomSerializer)) to your component. Whenever your component is saved, this custom serializer will detect the problem, fix it, and force you to save again whenever this issue is about to occur.

如果您的项目发生了很多,那么有一个非常难看的解决方法。将此VB或C#VisualStudioWorkaroundSerializer类添加到您的解决方案中。然后将属性DesignerSerializer(GetType(VisualStudioWorkaroundSerializer),GetType(CodeDomSerializer))添加到组件。每当保存组件时,此自定义序列化程序将检测到问题,修复它,并强制您在此问题即将发生时再次保存。

#3


Interesting glitch! It does indeed sound like a bug in one version of the designer / templating. Of course, if you think the designer code had been tampered, all bets are pretty-much off anyway...

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

However, in VS2008, it generates the undoubtably correct version:

但是,在VS2008中,它会生成无可置疑的正确版本:

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

So the base Dispose(...) is called. I haven't got VS2005 handy to test it, unfortunately. However - it doesn't initialize the components until it has to - the declaration is:

所以调用了基础Dispose(...)。不幸的是,我还没有得到VS2005进行测试。但是 - 它不会初始化组件,直到它必须 - 声明是:

private System.ComponentModel.IContainer components = null;

And then if it is needed, it is populated in InitializeComponent:

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

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

I guess with this construct it only has to maintain InitializeComponent (and not the fields itself).

我猜这个构造只需要维护InitializeComponent(而不是字段本身)。

#4


I have seen this happen, and I've also occasionally got warnings about from the Dispose method about components either never having its value assigned, or not being defined.

我已经看到这种情况发生了,而且我偶尔也会从Dispose方法得到有关组件的警告,这些组件要么从未分配其值,要么未定义。

I think it is a combination of two things:

我认为这是两件事的组合:

  1. Slightly different code generation between versions of Visual Studio
  2. Visual Studio版本之间的代码生成略有不同

  3. The Dispose method is only generated if there is not one already in the file, whereas InitializeComponent (and associated declarations) is generated each time
  4. 只有在文件中没有已经存在的情况下才会生成Dispose方法,而每次都会生成InitializeComponent(和关联的声明)

This results in an InitializeComponent/declarations section that is out-of-whack with the Dispose method.

这导致InitializeComponent / declarations部分与Dispose方法不同。