Почему Visual Studio IDE иногда инициализирует объект this.components: а иногда и нет?

Недавно я заметил некоторое поведение с Visual Studio Designer (С#), которое я не понимаю, и задавался вопросом, может ли кто-нибудь уточнить...

Один из моих 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);
        }
    }

то есть. Ничего не вызвано, если утилизация не верна, а компоненты не являются нулевыми.

В некоторых других формах эта первая строка в коде, сгенерированном конструктором, отсутствует. В этих случаях вызов base.Dispose находится вне условия "if" как таковой...

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

Я заметил это при отслеживании ошибки с формой, не закрывающейся, где this.components был нулевым, но вызов base.Dispose находился внутри этого условия (я подозреваю, что код дизайнера был подделан, но эта другая история.

Что контролирует это поведение?

(Некоторые более ранние формы в проекте были созданы в VS 2005, и теперь мы используем VS 2008 - ключ?)

Ответ 1

Это воспроизводимое поведение. Когда вы создаете новую форму, она начинается с скелета, который включает вызов конструктора this.components. Когда вы затем добавляете компонент (скажем, таймер) и удаляете его снова, дизайнер регенерирует код, теперь без вызова конструктора. Это не ошибка.

Fwiw, скелетный код генерируется Common7\IDE\ItemTemplates\CSharp\Windows Forms\1033\Form.zip\form.designer.cs

Увидеть вызов base.Dispose() внутри оператора if() является ошибкой. Это может быть самоиндуцированным. Или это может быть бета-версия скелетного кода. VS2005 делает все правильно. Проверьте папку ItemsTemplatesCache.

Ответ 2

6 лет спустя, и эта проблема все еще происходит. Мне удалось выявить хотя бы одну причину этого.

При тестировании, если ваш компонент имеет конструктор, который принимает IContainer, System.ComponentModel.Design.Serialization.ComponentCodeDomSerializer кэширует ссылку на тип IContainer для вашего проекта. Если затем сохранить объект для другого проекта в рамках одного и того же решения или, возможно, когда вы внесли некоторые другие изменения в свой проект, ComponentCodeDomSerializer больше не сможет найти конструктор, поскольку тип IContainer больше не будет равен кешированному типу.

Если это происходит много для вашего проекта, есть очень уродливое обходное решение. Добавьте VB или класс С# VisualStudioWorkaroundSerializer к вашему решению, Затем добавьте атрибут DesignerSerializer(GetType(VisualStudioWorkaroundSerializer), GetType(CodeDomSerializer)) к вашему компоненту. Всякий раз, когда ваш компонент сохраняется, этот пользовательский сериализатор обнаруживает проблему, исправляет ее и заставляет вас сохранять снова всякий раз, когда эта проблема возникает.

Ответ 3

Интересный глюк! Это действительно звучит как ошибка в одной версии конструктора/шаблонов. Конечно, если вы считаете, что код дизайнера был взломан, все ставки в любом случае все равно...

Однако в 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 (а не сами поля).

Ответ 4

Я видел, что это произошло, и я также иногда получал предупреждения о методе Dispose о компонентах, которые никогда не имеют назначенного значения или не определены.

Я думаю, что это комбинация двух вещей:

  • Немного отличается генерация кода между версиями Visual Studio
  • Метод Dispose генерируется только в том случае, если в файле уже нет, тогда как InitializeComponent (и связанные объявления) генерируется каждый раз

Это приводит к разделу InitializeComponent/declarations, который не работает с методом Dispose.