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

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

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

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

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

【问题讨论】:

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

标签: c# oop circular-dependency


【解决方案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】:

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

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

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

    右键单击项目引用并选择“添加引用...”。然后在出现的对话窗口中切换到“浏览”选项卡和“浏览”按钮。从那里您可以找到 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;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-15
          • 1970-01-01
          • 2021-01-14
          • 1970-01-01
          • 2019-09-12
          • 2016-07-10
          相关资源
          最近更新 更多