如何解决循环引用?

Posted

技术标签:

【中文标题】如何解决循环引用?【英文标题】:How to solve circular reference? 【发布时间】:2011-10-19 04:39:42 【问题描述】:

您如何解决循环引用问题,例如 A 类将 B 类作为其属性之一,而 B 类将 A 类作为其属性之一?

架构师如何解决这类问题?

如果以NHibernate为例,对象之间会有父子关系。

它如何处理那些父子场景?

【问题讨论】:

一开始就不要让类相互引用。 为什么首先要引入循环引用?设计父子关系必然意味着不应该存在循环引用。 【参考方案1】:

在大多数情况下,当我不得不让两个事物相互引用时,我创建了一个接口来删除循环引用。例如:

之前

public class Foo

    Bar myBar;


public class Bar

    Foo myFoo;

依赖图:

Foo     Bar
 ^       ^
 |       |
Bar     Foo

Foo 依赖于 Bar,但 Bar 也依赖于 Foo。如果它们位于单独的程序集中,则构建时会遇到问题,尤其是在进行干净的重建时。

之后

public interface IBar



public class Foo

    IBar myBar;


public class Bar : IBar

    Foo myFoo;

依赖图:

Foo, IBar     IBar
    ^          ^
    |          |
   Bar        Foo

Foo 和 Bar 都依赖于 IBar。没有循环依赖,如果将 IBar 放在自己的程序集中,Foo 和 Bar 在单独的程序集中将不再是问题。

【讨论】:

已经有几个星期了——你想要什么示例代码?使用上述接口的示例? 上面贴了示例代码。如果还有其他你想要的东西,我很困惑,不明白。您在寻找更具体的例子吗? 这并不能真正解决循环依赖的问题。这两个实例仍将直接相互引用。此外,foobar 可能是两个完全不同的模块,它们完全没有共同的概念(但仍然合作完成给定的任务)。在这种情况下,让它们实现相同的接口将是一种糟糕的做法。 @Powerslave 最近的评论不是来自我,但我认为你没有抓住重点。你不能总是重新设计别人的代码。我同意拥有任何类型的循环引用都不是最佳实践,但这样说并不能回答 OP 的问题,也不能帮助其他陷入这种情况的人。我的回答有,所以很有用。 是的,你没有抓住重点。当您无法控制第三方代码时,就没有“尝试”,并且您的辱骂(例如,“作为负责任的专业人士......”)不会改变这一点。你对这个问题的回答是非回答。这不是建设性的,这种重复的讨论也不是。【参考方案2】:

我会告诉你的朋友,他需要重新考虑他的设计。像您描述的循环引用通常是设计缺陷的代码味道。

【讨论】:

我真的不明白为什么它会是一个设计缺陷。您可以以 XElement 对象为例。 XElement1 有一个父节点 XElement2,这个父节点包含 XElement1。 @Jean-Philippe 大多数情况下,这是一个设计缺陷;不总是。即使在您的示例中,也没有循环引用,因为它们都是 XElement。 Anton Gogolev 的例子就是 OP 所说的。这是糟糕设计的一个例子。【参考方案3】:

与 C++ 不同(例如),C# 不需要前向声明来解析循环引用。因此:

public class A

    public B B  get;set; 


public class B

    public A A  get;set; 

但是,这通常表明设计决策存在问题。

【讨论】:

【参考方案4】:

在大多数情况下,最好的解决方案是更改您的设计并避免循环依赖。例如,您可以执行以下操作之一:

    将公共引用的代码移至解决方案中的实用程序项目,并让其他项目引用该实用程序项目 使用“Ed Bayiates”在他的回答中解释的界面。 如果它是少量的简单/通用代码,则为其中一个类重写它,这样您就不需要在循环依赖中引用它。 (我最不喜欢的)

但是,如果您正在使用包含许多项目的解决方案,并且由于您不拥有代码而无法进行上述更改之一,那么实施起来会很困难,或者不值得花时间修复,那么你可以使用这个方法:

右键单击项目引用并选择“添加引用...”。然后在出现的对话窗口中切换到“浏览”选项卡和“浏览”按钮。从那里您可以找到 DLL 并选择它。这充其量是一种解决方法,并且可能会导致构建问题,尤其是在两个 DLL 都被频繁更新和/或具有许多依赖项的情况下。我不推荐这种方法,但它可以在紧要关头。

【讨论】:

【参考方案5】:

接口是一个好主意,但是如果您正在寻找一种比重做很多东西的架构更快的解决方案,请尝试构建一个包含所有数据结构的 dll 类库,您的主项目包含需要这些数据的 UI,然后是任何其他的您要添加的 dll 也可以访问该数据结构 dll,因此它们拥有运行所需的所有信息,但仍然可以分开 - 这称为三力设计模式 -

【讨论】:

【参考方案6】:

当两个或多个相互依赖的资源导致锁定条件时发生循环引用。这使资源无法使用。

要处理 C# 中的循环引用问题,您应该使用垃圾回收。它检测并收集循环引用。垃圾收集器从本地和静态开始,它标记每个可以通过其子对象访问的对象。

通过这个,你可以处理循环引用的问题。

假设以下类在循环引用中。在这里它们都相互依赖 -

public class A
        
            B Two;
        
public class B
        
            A one;
        

为了解决这个问题,创建一个接口 -

public interface myInterface 


public class A 
   myInterface Two;


public class B: myInterface 
   A one;

【讨论】:

以上是关于如何解决循环引用?的主要内容,如果未能解决你的问题,请参考以下文章

Spring 循环引用AbstractFactoryBean 如何解决循环依赖

如何解决休眠双向映射导致的json序列化器中的循环引用?

Spring如何处理循环引用

如何解决检测到的循环引用

iOS底层探索之Block——如何解决Block循环引用问题?

Spring — 循环依赖