循环类引用问题

时间:2010-10-17 21:52:22

标签: c# circular-dependency

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

Employee对象具有Organization作为内部成员,Organization对象具有Employee成员以指示组织领导者。

此设置是否会导致无限循环实例化?

编辑:

我只是尝试运行代码,似乎有问题。 employee对象实例化Organization对象,Organization对象尝试实例化employee对象。它们都连接到数据库以填写其详细信息

这种情况一直持续到我的SQL服务器用尽连接为止。有什么替代我正在做的事情吗?

3 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分: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;
  }
}

请注意,并非发明依赖注入来解决此问题,但它可以在这种情况下工作。有了它,你可以放心,因为你知道你再也不会遇到这个问题了。

答案 2 :(得分:1)

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

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

org.GetAbsolutelyAllInformation();

你会打电话给

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

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

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