34

How do you solve circular reference problems like Class A has class B as one of its properties, while Class B has Class A as one of its properties?

How to do architect for those kind of problems?

If you take an example of NHibernate, there will be a parent-child relationship between objects.

How is it able to handle those parent child scenarios?

cchamberlain
  • 17,444
  • 7
  • 59
  • 72
user145610
  • 2,949
  • 4
  • 43
  • 75

6 Answers6

47

In most cases when I've had to have two things reference each other, I've created an interface to remove the circular reference. For example:

BEFORE

public class Foo
{
    Bar myBar;
}

public class Bar
{
    Foo myFoo;
}

Dependency graph:

Foo     Bar
 ^       ^
 |       |
Bar     Foo

Foo depends on Bar, but Bar also depends on Foo. If they are in separate assemblies, you will have problems building, particularly if you do a clean rebuild.

AFTER

public interface IBar
{
}

public class Foo
{
    IBar myBar;
}

public class Bar : IBar
{
    Foo myFoo;
}

Dependency graph:

Foo, IBar     IBar
    ^          ^
    |          |
   Bar        Foo

Both Foo and Bar depend on IBar. There is no circular dependency, and if IBar is placed in its own assembly, Foo and Bar being in separate assemblies will no longer be an issue.

Ed Bayiates
  • 11,060
  • 4
  • 43
  • 62
  • public interface IBar { } public class foo { IBar myBar; } public class bar : IBar { foo myFoo; } can u send any sample code, like u have explained before – user145610 Aug 04 '11 at 14:32
  • 1
    It's been a few weeks -- what sample code did you want? An example of using an interface like the above? – Ed Bayiates Aug 26 '11 at 19:52
  • Can you please let me know exactly what it is you want me to send? – Ed Bayiates Apr 25 '12 at 21:35
  • 2
    The example code is posted above. If there was something else you wanted, I'm confused and don't understand. Are you looking for a more concrete example? – Ed Bayiates Apr 26 '12 at 21:12
  • 2
    This doesn't really solve the problem of circular dependency. The two instances would still be directly referencing each other. Also, `foo` and `bar` might be two completely different modules having absolutely no common concept (yet still cooperating to achive a given task). In that case, having them implement the same interface would be a terrible practice. – Powerslave Jul 20 '15 at 10:22
  • 1
    @Powerslave, I don't think you understand my answer. It most certainly does solve the problem as the two instances are not directly referencing each other. This solution is used by my company, and it does work. I will add a little more info to the answer. – Ed Bayiates Jul 20 '15 at 23:31
  • 1
    @EdBayiates I've re-read your answer several times, and every single word I wrote still holds true. Your post might still be missing a great deal of detail. Also, please pay attention to the usage of the words *type/class*, *instance* and *reference*. In your example, *types* do not depend on each other, thanks to the interface. *Instances*, however, do directly *reference* each other, which is, when talking about dependencies, clearly circular and is pretty much a serious issue. With a properly implemented DI, such a condition would result in miserable failure in the majority of cases. – Powerslave Jul 21 '15 at 09:47
  • I'm sorry Powerslave but I don't agree. – Ed Bayiates Jul 21 '15 at 15:02
  • @EdBayiates Well, you've got all the rights to disagree. Yet, if you have to, like in your example, instantiate `A` to be able to instantiate `B`, and also have to instantiate `B` before you can instantiate `A`, you pretty much failed solving the problem of circular dependency. You could arguably use non-final fields and setters to get around that problem, but that'd be a hack, not a solution. (For some odd reason, I have a feeling you'll... errr... _disagree_ with that as well) – Powerslave Jan 22 '18 at 09:02
  • 1
    @rolls Designing around such problems seems, at least for the moment being, quite dependent on the particular situation to solve. In most cases, circular dependencies are a code smell and can be avoided altogether by getting your higher level design right. In other cases, you cannot help, but come up with an acceptable workaround. One of the main goals here is the classes not needing an existing instance of each other for instantiation. Maybe if I get to have the time for it, I'll try to post a more detailed answer, thanks for your suggestion! – Powerslave Jan 22 '18 at 09:12
  • 3
    @Powerslave the recent comment didn't come from me, but I think you're missing the point. You can't always redesign someone else's code. I agree that having any kind of circular reference is not a best practice, but saying so doesn't answer the OP's question and doesn't help anyone else stuck in this situation. My answer does, so it's useful. – Ed Bayiates Jan 22 '18 at 17:12
  • @EdBayiates I'm pretty much getting your point, sir, I just am in total disagreement with you. You may not always have the option to redesign already existing mess, but as a responsible professional you should always go out your way and try to, and if you absolutely can't then your best bet is a messy workaround - if resolvable at all. Now, with all my respect, your proposition _does not at all solve the problem of circular dependency as each class still needs an instance of the other all the same_ (and such a misuse of interfaces is quite horrifying anyways, sorry). – Powerslave Jan 26 '18 at 10:33
  • 4
    Yes, you are missing the point. There is no "try" when you don't have control of third-party code, and your name-calling (e.g., "as a responsible professional...") doesn't change that. Your answer to the issue is a non-answer. It isn't constructive, and neither is this repetitive discussion. – Ed Bayiates Jan 26 '18 at 18:27
7

I would tell your friend he needs to rethink his design. Circular references like you describe are often a code smell of a design flaw.

John Kraft
  • 6,811
  • 4
  • 37
  • 53
  • 6
    I don't really understand why it would be a design flaw. You can take the XElement object as an exemple. XElement1 have a parent node XElement2 and this parent contains XElement1. – Jean-Philippe Leclerc Aug 03 '11 at 14:59
  • 2
    @Jean-Philippe Most often, it is a design flaw; not always. Even in your example, there's no circular reference because they are both XElements. Anton Gogolev's example is what the OP was talking about. That's an example of bad design. – John Kraft Aug 03 '11 at 15:30
7

Unlike C++ (for instance), C# does not need forward declarations to resolve circular references. Hence:

public class A
{
    public B B { get;set; }
}

public class B
{
    public A A { get;set; }
}

However, this is often an indicator of questionable design decisions.

Anton Gogolev
  • 113,561
  • 39
  • 200
  • 288
3

In most every case the best solution is to change your design and avoid a circular dependency. For instance you may do one of the following:

  1. Move the common referenced code to a utility project in your solution and have the other projects reference the Utility project
  2. Use an interface as explained by "Ed Bayiates" in his answer.
  3. If its a small amount of simple/common code then rewrite it for one of the classes so you don't need to reference it in a circular dependency. (my least favorite)

However, if you are working in a solution with many projects and you don't have the ability to make one of the changes above because you don't own the code, its to difficult to implement, or not worth the time to fix, then You can use this method:

Right-click on the project references and select "Add Reference...". Then in the dialog window that appears switch to the "Browse" tab and the "Browse" button. From there you can go find the DLL and select it. This is a work around at best and can cause build problems especially if both DLLs are being updated frequently, and/or have many dependencies. I do not recommend this method but it works in a pinch.

Fissh

gadildafissh
  • 2,243
  • 1
  • 21
  • 26
0

interfaces are a good idea however if your looking for a quicker solution than redoing the architecture of so many things try building one dll class library that holds all your data structures your main project holds your UI that needs that data and then any other dlls you want to add can access that data structures dll as well so they have all the info they need to run but still can be separate- this is called the tri force design pattern -

-1

Circular reference occurs when two or more interdependent resources cause lock condition. This makes the resource unusable.

To handle the problem of circular references in C#, you should use garbage collection. It detects and collects circular references. The garbage collector begins with local and static and it marks each object that can be reached through their children.

Through this, you can handle the issues with circular references.

Let’s say the following classes is in circular reference. Here both of them depends on each other −

public class A
        {
            B Two;
        }
public class B
        {
            A one;
        }

To solve the issue, create an interface −

public interface myInterface {
}

public class A {
   myInterface Two;
}

public class B: myInterface {
   A one;
}
Anshul
  • 9
  • 4