Циркулярная зависимость в двух проектах в С#

У меня есть два проекта в решении с именем ProjectA (ConsoleApplication) и ProjectB (ClassLibrary). ProjectA имеет ссылку на ProjectB. Вообще говоря, ProjectA вызывает метод в ProjectB, чтобы сделать некоторые вещи и вернуть результаты ProjectA. Иногда, однако, мне нужно, чтобы ProjectB отправил некоторую "дополнительную" информацию в ProjectA (более конкретно, чтобы вызвать метод Console.WriteLine() в ProjectA). Для этого мне нужно передать ProjectA в ProjectB, но когда я попытаюсь это сделать, я получаю следующую ошибку:

A reference to ProjectA could not be added. Adding this project as a reference would cause a circular dependency.

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

Ответ 1

Я предлагаю вам использовать события и слушателей. Например, вы можете отправлять сообщения из ProjectB через Trace.WriteLine, а в ProjectA - добавить подписчика на трассировку..NET уже предлагает класс ConsoleTraceListener для маршрутизации сообщений Trace на консоль. Вы можете добавить слушателя из ProjectA через:

Trace.Listeners.Add(new ConsoleTraceListener());

В качестве альтернативы, если вы не хотите использовать интегрированные классы, вы можете создать очень простой "исходный" класс в ProjectB, который выведет событие с Action<string> в качестве своей подписи (хотя я бы предложил вам создать делегат для него), а затем подписаться на него от ProjectA. Как правило, классы .NET более гибкие.

ProjectB

public static class MyTrace
{
    public static event Action<string> MessageReceived;

    internal static void Broadcast(string message)
    {
        if (MessageReceived != null) MessageReceived(message);
    }
}

ProjectA

MyTrace.MessageReceived += s =>
{
    /*Operate*/
};

Ответ 2

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

Существует несколько способов решения этой проблемы, некоторые из которых были упомянуты в других ответах. Еще не опубликовано, чтобы полностью устранить зависимость между проектом A и проектом B и создать третий проект C, который определяет интерфейсы, с которыми взаимодействуют A и B. То есть:

namespace C
{
    public interface IFoo { void Frob(); }
    public interface IBar { void Qux(); }
}

И затем сделайте проекты A и B справочного проекта C и сделайте их классы реализованы IFoo, IBar и т.д. Когда метод в проекте A должен вызывать Frob на объекте в проекте B, он делает это, получая IFoo, а не получая некоторый класс в B.

Это имеет смысл?

Ответ 3

Вы не можете этого сделать. Если проекты называют друг друга, они должны быть в одном проекте. Или, вместо ProjectB, вызывающего ProjectA, ProjectB может сделать его общедоступным, поэтому ProjectA может получить к нему доступ.

У вас не может быть круговой зависимости. Как ты мог? Как компилятор знает, что строить первым? У вас есть фундаментальная проблема дизайна и что нужно исправить.

Ответ 4

Я бы создал собственное событие в ClassB

public event EventHandler MySpecialHook;

EventHandler является стандартным делегатом

public delegate void EventHandler(object sender, EventArgs e);

Затем в вашем классе A после создания вашего экземпляра ClassB подключитесь к обработчику событий для уведомления, когда что-то происходит в B, о котором должен знать A. Очень похоже на OnActivated, OnLostFocus, OnMouseMove или подобные события (но они делегируют разные подписи)

public class ClassB {

public event EventHandler MySpecialHook;

public void SomeMethodDoingActionInB()
{

    // do whatever you need to.
    // THEN, if anyone is listening (via the class A sample below)
    // broadcast to anyone listening that this thing was done and 
    // they can then grab / do whatever with results or any other 
    // properties from this class as needed.
    if( MySpecialHook != null )
        MySpecialHook( this, null );
 } 
}

public class YourClassA
{

   ClassB YourObjectToB;

   public YourClassA
   {
      // create your class
      YourObjectToB = new ClassB();
      // tell Class B to call your "NotificationFromClassB" method
      // when such event requires it
      YourObjectToB += NotificationFromClassB;
   }

   public void NotificationFromClassB( object sender, EventArgs e )
   {
      // Your ClassB did something that your "A" class needs to work on / with.
      // the "sender" object parameter IS your ClassB that broadcast the notification.
   }
}