Вас волнуют циклические ссылки или циклические зависимости? Циркулярные ссылки (проблема времени выполнения) очень трудно избежать при использовании шаблона наблюдателя. От круговых зависимостей (проблема времени проектирования) всегда можно избавиться.
Типичное использование шаблона наблюдателя в c # - для наблюдателя иметь ссылку на объект издателя и зарегистрировать себя в событии, используя что-то вроде:
publisherObject.SomeEvent += MyEventHandler();
Таким образом, у наблюдателя уже есть ссылка на publisherObject, и в фоновом режиме происходит то, что publisherObject получает (и сохраняет) ссылку на обработчик события наблюдателя. Так что, если вы не собираетесь немедленно удалить ссылку на publisherObject, вы застряли с циклической ссылкой.
Обычно это проблема, когда вы хотите, чтобы сборщик мусора убрал неиспользуемые объекты. «Скрытой» ссылки на обработчик событий наблюдателя внутри publisherObject достаточно, чтобы предотвратить сборщик мусора. Это иногда называют проблемой потерянного слушателя. Самый простой способ сделать это - поместить отписки о событиях в метод Dispose () наблюдателя (и не забывать вызывать его, когда вы избавляетесь от наблюдателя).
Вы можете обойти это, но это обычно добавляет довольно много сложности, которая не может быть оправдана в небольшом приложении. Предыдущие плакаты уже предлагали маршруты EventBroker, MEF и внедрения зависимостей.
Если вас больше волнуют циклические зависимости, то самый простой ответ - строгая иерархия родитель-потомок, как предложил Quarrelsome. Родитель (наблюдатель) всегда знает о своих дочерних элементах, поэтому может вызывать свойства и методы для них напрямую, если это необходимо. Дети (издатели) не должны ничего знать о своих родителях. Они общаются вверх исключительно через события и возвращают значения функций. Затем связь между детьми направляется через общего родителя (события на пути вверх, вызовы методов на пути вниз).
Обратите внимание, что у вас все еще есть циклические ссылки из-за того, как работает механизм событий (поэтому будьте осторожны с удалением), но у вас нет циклических зависимостей. Дочерний объект может даже находиться в совершенно другой сборке, которая не имеет ссылки на время разработки, которая содержит родительскую.
Обратите внимание, что вы можете быть немного гибкими в отношении того, что вы считаете родителем, а что ребенком. Тот факт, что ваша основная форма создает вложенную форму, не означает, что вы должны определять связи между родителями и детьми в этом направлении. Типичная архитектура стиля плагина может иметь основную форму создания экземпляров плагинов. Но после создания сообщения, вероятно, будут воспринимать плагин как родитель / наблюдатель, а основную форму - как источник публикации / события.