How to solve circular reference?
Asked Answered
T

6

36

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?

Thermion answered 3/8, 2011 at 14:48 Comment(2)
Don't have the classes reference each other to begin with.Healy
Why would you introduce a circular reference in the first place? Designing parent-child relationships necessarily means no circular references ought to exist.Stahl
T
48

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.

Traherne answered 3/8, 2011 at 16:14 Comment(13)
public interface IBar { } public class foo { IBar myBar; } public class bar : IBar { foo myFoo; } can u send any sample code, like u have explained beforeThermion
It's been a few weeks -- what sample code did you want? An example of using an interface like the above?Traherne
Can you please let me know exactly what it is you want me to send?Traherne
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?Traherne
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.Capet
@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.Traherne
@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.Capet
I'm sorry Powerslave but I don't agree.Traherne
@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)Capet
@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!Capet
@Capet 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.Traherne
@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).Capet
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.Traherne
O
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.

Owl answered 3/8, 2011 at 14:51 Comment(2)
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.Sorosis
@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.Owl
D
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.

Downgrade answered 3/8, 2011 at 14:52 Comment(0)
G
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

Gemmulation answered 17/2, 2017 at 1:56 Comment(0)
K
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 -

Kun answered 26/10, 2017 at 14:30 Comment(0)
F
-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;
}
Fredericksburg answered 17/6, 2019 at 9:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.