【问题标题】:Circular class reference problem循环类引用问题
【发布时间】:2010-10-17 21:52:22
【问题描述】:

我正在开发一个 C# 应用程序,并且有一个 Employee 类和一个 Organization 类。

一个 Employee 对象有一个 Organization 作为内部成员,一个 Organization 对象有一个 Employee 成员来指示 Org 领导。

这种设置是否会导致无限循环实例化的问题?

编辑:

我刚刚尝试运行代码,但似乎有问题。员工对象实例化组织对象,组织对象尝试实例化员工对象。他们都连接到数据库以填写他们的详细信息

这种情况一直在发生,直到我的 SQL 服务器连接不足。除了我正在做的事情之外,还有其他选择吗?

【问题讨论】:

  • 您是在使用一些对象关系映射器还是您自己编写了数据库访问代码?
  • 循环引用不是C#的问题。这些工作正常,加载它们的代码已损坏。你应该使用一个工厂来返回已经加载的实体而不重新实例化它们或类似的东西。
  • 循环依赖通常表明存在设计缺陷。这表明这两个对象一开始就不应该被分割。一种方法可以做到这一点,但仍然保持您的代码分开,将它组合成三个对象:一个组合对象,另外两个简单的数据访问对象,他们唯一的工作是从数据库中获取数据。这两个数据访问对象不会相互了解。

标签: c# circular-dependency


【解决方案1】:

没有。它会编译得很好。 C# 编译器足够聪明,可以考虑所有类型,即使它们“尚未编译”。与 Java 一样,定义/声明是一回事(这与 C/C++ 不同)。

【讨论】:

  • 它会编译文件,但它会一直运行,直到我的 SQL 服务器连接不足。见编辑
  • 虽然您的答案是正确的,但关于引用类型的默认初始化,您错过了他问题的一个关键组成部分:“这种设置是否会导致无限循环实例化的任何问题? "
【解决方案2】:

这种设置是否会导致无限循环实例化的问题?

引用类型默认为null。除非您实例化一个新实例并将其分配给该变量,否则不会为该类型调用任何代码。

每个构造函数都构造另一个对象的实例吗?他们将来有可能(可能是意外)吗?如果是这样,那么是的,您可以点击您正在谈论的场景:

class A
{
  public A()
  {
    this.B = new B();
  }
  public B B;
}

class B
{
  public A A = new A(); // This is the same as instantiating the object in the ctor
}

// ...

A obj = new A(); // This calls new B, which calls new A, repeat ad infinitum

您可以通过在构造函数之外实例化另一个对象的实例来打破这样的循环。例如,首次访问时:

class A
{
  public A()
  {
  }

  public B B
  {
    get
    {
      if(b == null)
        b = new B();
      return b;
    }
  }

  private B b;
}

class B
{
  public B()
  {
  }

  public A A
  {
    get
    {
      if(a == null)
        a = new A();
      return a;
    }
  }

  private A a;
}

// ...

A obj = new A(); // new B doesn't get called yet
obj.B.Something(); // ... Now it does...

您仍然需要注意class A 不会在其构造函数中访问它自己的this.B 属性,反之亦然。不过,只要不在构造函数中调用这些方法,您就可以在方法中访问这些属性。

另一种选择是进行依赖注入。这是您将依赖项传递给对象的地方,而不是让它实例化对象本身:

class A
{
  public A(B b)
  {
    this.B = b;
  }

  public B B;
}

class B
{
  public B(A a)
  {
    this.A = a;
  }

  public A A;
}

// ...

A someA = new A(
  new B(
    null)); // You have to break the cycle somewhere...
someA.B.A = someA;

// or ...

class ABFactory
{
  public static A CreateA(/* options for a */, /* options for b */)
  {
    A result = new A(
      new B(
        null));
    result.B.A = result;
    return result;
  }
}

请注意,依赖注入并不是为了解决这个问题而发明的,但它可以在这种情况下工作。有了它,您就可以高枕无忧了,因为您知道再也不会遇到这个问题了。

【讨论】:

  • 为什么不A someA = new A(new B(someA))?这在 C# 中有效吗?
  • @Christian Mann:我不确定(这可能是未定义的行为?)但它可能会做同样的事情 - 将null 传递给new B(),因为someA直到 new B 被执行后才被构造。
【解决方案3】:

您遇到的问题不太可能是 C# 本身,而可能是您的代码进入了无限循环,其中组织向员工询问信息,导致员工再次向组织询问相同的信息。 .

您需要做的就是断开此链接,以便请求有关组织的信息不会将呼叫链接到其员工(反之亦然)。即,如果您想要有关组织的完整信息,而不是

org.GetAbsolutelyAllInformation();

你会打电话

org.GetOrganisationInformation();
org.Leader.GetEmployeeInformation();

如果你必须一次性返回信息,那么不要在一个新对象中返回信息,而是传入一个“空白”对象来填充。如果你被一个对象调用已经填写的,返回缓存的信息,而不是再次从数据库中获取。这样,调用方法的顺序无关紧要,它们只会在您传入的对象中填写组织或领导者信息,并且在下一次迭代时发现所有的“循环”将停止信息已填写完毕。

一般而言,首先降低此类循环发生风险的一种方法是将组织的“领导者”作为 ID(例如员工编号)而不是直接参考。这鼓励任何使用此代码的程序员获取领导者 ID,然后作为单独的步骤查找领导者的信息。

【讨论】:

  • 如果你仔细阅读他的问题,他表明这(目前)只是实例化的问题。但你是对的,这是他将来需要注意的问题,并且对于任何类型的循环依赖都是危险的。
猜你喜欢
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-22
  • 2021-12-20
相关资源
最近更新 更多