C # События / Подписки .... слушать проекты без ссылок - PullRequest
1 голос
/ 15 декабря 2008

Я разрабатываю приложение, пытающееся использовать шаблон Observer. В основном у меня есть базовая форма, из которой можно загружать различные компоненты (формы).

Базовая форма ссылается на каждый из компонентов, а некоторые компоненты ссылаются друг на друга.

Если я хочу, чтобы один из компонентов прослушивал события, вызванные базовой формой (возможно, из меню и т. Д.), Я не могу достичь этого без добавления ссылки на базовую форму в компоненте. Это вызывает "круговую ссылку".

Можно ли прослушивать / подписываться на события в проектах, на которые нет ссылок?

Ответы [ 6 ]

1 голос
/ 15 декабря 2008

Ваша структура должна определять интерфейсы, которые используются для подключения (обратите внимание, что события могут быть определены в интерфейсах, чтобы вы могли продвигать события в интерфейс). Совет Майка Скотта о «зависеть от абстракций, а не от реализаций» очень важен, но я был бы немного сильнее здесь - вы должны программировать по контракту и использовать слоистый дизайн .

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

1 голос
/ 15 декабря 2008

Корпорация Майкрософт разработала «Платформу управляемой расширяемости (MEF)», которая может работать для вас. Из обзора MEF:

MEF предоставляет стандартному способу хост-приложения выставлять себя и использовать внешние расширения. Расширения по своей природе могут повторно использоваться в различных приложениях. Тем не менее, расширение может быть реализовано в зависимости от приложения. Сами расширения могут зависеть друг от друга, и MEF позаботится о том, чтобы они были соединены в правильном порядке (еще одна вещь, о которой вам не придется беспокоиться).

Обзор и загрузки для MEF находятся по адресу http://www.codeplex.com/MEF/Wiki/View.aspx?title=Overview&referringTitle=Home

1 голос
/ 15 декабря 2008

Вы можете решить это различными способами. Один простой способ - создать специальный класс, который знает о базовой форме и различных компонентах. Этот класс отвечает за создание формы и компонентов. Поскольку он знает о них, он может прикреплять обработчики событий к соответствующим событиям. По сути, он просто «подключает» методы обработчика событий к компонентам к событиям в базовой форме.

Другой способ - определить интерфейс с событиями, которые будут реализованы основной формой. Компонентам можно передать экземпляр этого интерфейса в своих конструкторах. Затем они могут присоединиться к обработчикам событий. Таким образом, компоненты знают только об интерфейсе, а не о базовой форме. Это применение принципа «зависит от абстракций, а не от реализаций». В этом случае базовая форма будет реализовывать интерфейс и обладать знаниями о компонентах, передавая себя им при их создании. Таким образом, зависимость является односторонней.

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

0 голосов
/ 18 декабря 2008

Просто создайте проект с классом для события:

public class BaseFormEventClass
{
    public EventHandler<EventArgs> BaseFormDidSomething;
}

Затем сделайте ссылку на этот проект как из базового проекта, так и из проекта компонентов. Позвольте базовой форме создать экземпляр класса событий и передать его всем загружаемым компонентам:

public class MyComponent
{
    public MyComponent(BaseFormEventClass eventClass)
    {
        eventClass.BaseFormDidSomething += this.EventClass_BaseFormDidSometing;
    }
    // ...
}

public class BaseForm
{
    private BaseFormEventClass eventClass = new BaseFormEventClass();

    private void LoadComponents()
    {
        MyComponent component1 = new MyComponent(this.eventClass);
    }

    private void RaiseBaseFormDidSomething()
    {
        EventHandler<EventArgs> handler = eventClass.BaseFormDidSomething;
        if (handler != null) handler(this, EventArgs.Empty);
    }
}
0 голосов
/ 16 декабря 2008

Вас волнуют циклические ссылки или циклические зависимости? Циркулярные ссылки (проблема времени выполнения) очень трудно избежать при использовании шаблона наблюдателя. От круговых зависимостей (проблема времени проектирования) всегда можно избавиться.

Типичное использование шаблона наблюдателя в c # - для наблюдателя иметь ссылку на объект издателя и зарегистрировать себя в событии, используя что-то вроде:

publisherObject.SomeEvent += MyEventHandler();

Таким образом, у наблюдателя уже есть ссылка на publisherObject, и в фоновом режиме происходит то, что publisherObject получает (и сохраняет) ссылку на обработчик события наблюдателя. Так что, если вы не собираетесь немедленно удалить ссылку на publisherObject, вы застряли с циклической ссылкой.

Обычно это проблема, когда вы хотите, чтобы сборщик мусора убрал неиспользуемые объекты. «Скрытой» ссылки на обработчик событий наблюдателя внутри publisherObject достаточно, чтобы предотвратить сборщик мусора. Это иногда называют проблемой потерянного слушателя. Самый простой способ сделать это - поместить отписки о событиях в метод Dispose () наблюдателя (и не забывать вызывать его, когда вы избавляетесь от наблюдателя).

Вы можете обойти это, но это обычно добавляет довольно много сложности, которая не может быть оправдана в небольшом приложении. Предыдущие плакаты уже предлагали маршруты EventBroker, MEF и внедрения зависимостей.

Если вас больше волнуют циклические зависимости, то самый простой ответ - строгая иерархия родитель-потомок, как предложил Quarrelsome. Родитель (наблюдатель) всегда знает о своих дочерних элементах, поэтому может вызывать свойства и методы для них напрямую, если это необходимо. Дети (издатели) не должны ничего знать о своих родителях. Они общаются вверх исключительно через события и возвращают значения функций. Затем связь между детьми направляется через общего родителя (события на пути вверх, вызовы методов на пути вниз).

Обратите внимание, что у вас все еще есть циклические ссылки из-за того, как работает механизм событий (поэтому будьте осторожны с удалением), но у вас нет циклических зависимостей. Дочерний объект может даже находиться в совершенно другой сборке, которая не имеет ссылки на время разработки, которая содержит родительскую.

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

0 голосов
/ 15 декабря 2008

Вы можете использовать шаблон EventBroker, как показано в Шаблоны и практики Microsoft Библиотека.

Однако я сам не уверен, насколько хорош этот шаблон, лично я предпочитаю создавать архитектуру, в которой объекты не ссылаются друг на друга (всегда сценарий родительский -> дочерний) и решают проблемы с зависимостями, а не игнорируют их. .

...