Объекты должны быть расположены в определенном порядке: это запах кода? - PullRequest
1 голос
/ 12 марта 2011

Контекст

Я работаю с приложением Winforms (.NET 3.5 SP1), которое имеет концепцию рабочих пространств, которая может содержать n номеров панелей.Каждая панель (происходит от Panel) имеет вид:

    .-----------------------.
    |Workspace              |
    |.--------.  .--------. |
    ||Panel1  |  |Panel2  | |
    ||.-----. |  |.-----. | |
    |||View1| |  ||View2| | |
    ||'-----' |  |'-----' | |
    |'--------'  '--------' |
    '-----------------------'

Все панели добавляются в коллекцию this.Controls класса Workspace (которая происходит от UltraTabPageControl, элемента управления Infragistics GUI).Каждое представление добавляется в коллекцию Controls их родителя.Таким образом, когда в рабочую область вызывается Dispose, панели и виды автоматически располагаются, что вполне ожидаемо и желательно.

У нас есть другая концепция, называемая ViewManager.Он отслеживает все элементы управления View в рабочей области и отвечает за поддержание единого «главного» представления.Всякий раз, когда создается View, он регистрируется у этого менеджера.Это добавляет View в список, а затем запускает некоторую логику для определения нового «основного» представления, а затем вызывает метод Synchronize() для каждого представления.

Текущий дизайн таков, что всякий раз, когда View.Dispose() называется, он отменяет регистрацию от ViewManager.Это удаляет View из списка, а затем запускает соответствующую логику, которая проверяет новый мастер, из оставшихся представлений.

Поворот

Когда мызакрывают все рабочее пространство, есть один специальный тип Panel, который должен быть закрыт перед другими панелями.Итак, в нашем методе Dispose есть код, который выглядит следующим образом:

protected override void Dispose(bool disposing)
{
    var theSpecialPanel = GetSpecialPanel();
    if (theSpecialPanel != null)
    {
        theSpecialPanel.Dispose();
    }
    base.Dispose(disposing);
}

Если мы уберем этот код, то другие панели могут быть расположены до theSpecialPanel.Это приводит к тому, что логика, которая проверяет наличие новой главной панели, вызывает Synchronize() на каждой View, включая эту специальную панель.Это выдает

"InvalidComObjectException: COM-объект, который был отделен от лежащего в его основе RCW, не может быть использован."

Вопрос

Является ли этот дизайнтакое запах кода?Странно ли добиваться того, чтобы определенный объект располагался раньше других?

Ответы [ 3 ]

2 голосов
/ 12 марта 2011

Вполне разумно требовать, чтобы определенные объекты располагались в определенной последовательности.Обратите внимание, что основным ограничением финализаторов является то, что они не могут гарантировать что-либо о порядке распоряжения.Рассмотрим объект Froboz9000Connection (используемый для управления подключением к компьютеру Froboz 9000), который, в свою очередь, содержит SerialPort, который используется для фактического взаимодействия.Прежде чем программа завершится, необходимо отправить определенную последовательность команд на удаленный компьютер.Правильная последовательность событий для метода Dispose объекта Frobozz9000Connection отправит необходимую последовательность команд, а затем утилизирует SerialPort.Если SerialPort удаляется первым, объект Frobozz9000Connection не сможет отправить правильную последовательность команд для уведомления удаленного компьютера о том, что его службы больше не нужны.

Эта проблема, кстати, еще одна (однаиз многих причин, по которым я не люблю финализаторы.Хотя бывают случаи, когда финализаторы, безусловно, могут быть полезны, я думаю, что в подавляющем большинстве случаев гораздо важнее просто убедиться, что Dispose используется правильно.

2 голосов
/ 12 марта 2011

Требует ли SpecialPane явного удаления?Или вы просто хотите убедиться, что вызов Synchronize() всегда правильный?Я предполагаю, что ваш View является производным от Control.

public void Synchronize()
{
    if (this.IsDisposed || this.Disposing) return; // or return a 'remove me' flag
    ... 
    // sync
}

Не зная остальной части вашего кода, я предполагаю, что это должно работать относительно хорошо, не полагаясь на Finalizer.Вы возлагаете на каждое представление ответственность за то, чтобы знать, могут ли они выполнять определенное действие, не требуя, чтобы контейнер формы знал о деталях реализации вашей системы.

Я полагаю, что это также поможет в обслуживании.Полагаться на порядок утилизации может быть сложно, и если придет другой разработчик, они могут пропустить это вообще.Он также будет работать, если представление удалено другими способами, а не вызывается из метода удаления формы.

О, и если представление не расширяет элемент управления, тогда укажите для View ссылку на своего родителя ипроверьте состояние утилизации на родительском контроле.

1 голос
/ 12 марта 2011

В вашем фрагменте кода я не видел, где вы располагаете другие панели.

Если вы утилизируете все панели, я не вижу проблем с принятием решения о каком-либо порядке утилизации.что вы хотите.Если я следил за вами правильно, вы можете сделать:

foreach (panel in Panels.Where(p => p != theSpecialPanel))
{
   panel.Dispose();
}
theSpecialPanel.Dispose();
...