13

I have two projects in a solution named ProjectA (ConsoleApplication) and ProjectB (ClassLibrary). ProjectA has a reference to ProjectB. Generally speaking, ProjectA calls a method in ProjectB to do some stuff and return the results to ProjectA. Sometimes however, I need ProjectB to send some "additional" information to ProjectA (more specifically to call the Console.WriteLine() method in ProjectA). To achieve this, I need to refer ProjectA in ProjectB, but when I try to do that, I get the following error:

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

I understand the whole coupling concept and it does make sense to get this message, however, I need to send additional information to ProjectA in some cases. Any ideas?

Arian Motamedi
  • 7,123
  • 10
  • 42
  • 82

4 Answers4

15

It is actually possible to create projects that have circular dependencies that compile successfully but I strongly recommend against it. Instead, organize your projects so that they have an acyclic dependency graph.

There are a number of ways to solve this problem, several of which have been mentioned in other answers. One not yet posted is to eliminate the dependency between project A and project B entirely, and create a third project, C, that defines the interfaces that A and B communicate over. That is:

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

And then make projects A and B reference project C, and make their classes implement IFoo, IBar, and so on. When a method in project A needs to call Frob on an object in project B, it does so by obtaining an IFoo, rather than obtaining some class in B.

Does that make sense?

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 4
    You've piqued my curiosity - how can you create projects with circular dependencies that compile successfully? – kvb Jan 29 '13 at 21:02
10

I suggest you to use events and listeners. You can, for example, send messages from ProjectB through Trace.WriteLine while, in ProjectA, you would add a subscriber for the trace. .NET already offers a ConsoleTraceListener class, to route Trace messages to the Console. You can add a listener from ProjectA through:

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

Alternatively, if you don't want to use the integrated classes, you can build a very simple "source" class in ProjectB which will exposes an event with Action<string> as its signature (although I'd suggest you to create a delegate for it), then subscribe to it from ProjectA. Generally, .NET classes are more flexible.

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*/
};
e_ne
  • 8,340
  • 32
  • 43
1

You can't do this. If the projects call each other, they should be in the same project. Or, instead of ProjectB calling ProjectA, ProjectB could make it's information public, so ProjectA could access it.

You can't have a circular dependency. How could you? How would the compiler know which to build first? You have a fundamental design issue and that's what needs to be fixed.

Bob Horn
  • 33,387
  • 34
  • 113
  • 219
0

I would actually create your own event on ClassB

public event EventHandler MySpecialHook;

EventHandler is a standard delegate of

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

Then, in your Class A, after creating your instance of ClassB, hook into the event handler for notification when something occurs in B that A should know about. Much like an OnActivated, OnLostFocus, OnMouseMove or similar events (but they have delegate different signatures)

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.
   }
}
iJay
  • 4,205
  • 5
  • 35
  • 63
DRapp
  • 47,638
  • 12
  • 72
  • 142