Циркулярная зависимость в двух проектах в С#
У меня есть два проекта в решении с именем 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.
}
}