在施工前发送对象的引用

我在我们的一个应用程序中看到了下面的代码:

public class First() { private Second _second; public First() { _second = new Second(this); // Doing some other initialization stuff, } } public class Second { public Second(First f) { } } 

First()构造函数中, 完全构造之前发送First()类的引用是否不坏? 我在想,只有当控制逻辑离开构造函数时,对象才被完全构造。

还是这样好吗?

我的问题是,在First()构造函数中,不是我们在完全构造之前发送First()类的引用吗?

有些。 当然,这可能是一个问题。

如果Second构造函数仅仅包含一个引用以供以后使用,那还不错。 另一方面,如果Second构造函数调用First

 public Second(First f) { f.DoSomethingUsingState(); } 

…国家还没有build立,那当然是一件非常不好的事情。 如果你在First上调用一个虚拟方法,那么它可能会更糟 – 你甚至可能调用一些代码,甚至没有机会运行它的任何构造函数体(尽pipe它的variables初始化器已经被执行)。

特别是, readonly字段可以先看到一个值,然后再看看另一个值。

我前段时间在这篇博文中提到过,这可能会提供更多的信息。

当然, 如果不做这样的事情,创build两个互相引用的不可变对象是相当困难的。

如果遇到这种模式,你可能会检查是否可以重构它:

 public class First() { private Second _second; public First() { _second = new Second(this); // Doing some other initialization stuff, } private class Second { public Second(First f) { } } } 

将依赖关系传递给构造函数意味着这两个类之间的紧密耦合,因为First必须相信Second知道它在做什么,并且不会试图依赖First的未初始化状态。 当Second是一个私有的嵌套子类(因此有一个明确的实现细节),或者当它是一个内部类时,这种强耦合更适合。

答案是,这取决于。 一般来说,由于潜在的后果,这将被认为是一个坏主意。

更具体地说,只要Second在构build之前实际上没有使用First任何东西,那么你应该没问题。 如果你不能保证,但肯定会遇到问题。

是的,这有点不好。 在完全初始化之前,有可能在First的领域做些事情,这会导致不希望的或未定义的行为。

当你从你的构造函数调用一个虚拟方法时,会发生同样的事情。

与例如C ++相反,CLR没有完全构build或不完全构build的对象的概念。 只要内存分配器返回一个归零对象,并且在构造函数运行之前,就可以使用(从CLR的angular度来看)。 它有它的最后一个types,调用虚拟方法调用最多的派生覆盖等。你可以在构造函数的主体中使用this ,调用虚拟方法等。这可能确实会导致初始化顺序的问题,但CLR中没有任何东西阻止它们。

诚然,这可能会导致你所描述的问题。 因此,一般build议运行诸如_second = new Second(this); 只有在你的评论暗示其他初始化的东西后。

有时候,这种模式是存储两个对象之间相互引用的唯一解决scheme。 然而,在许多情况下,这种情况的发生方式是接收可能未完全初始化的实例的类与被引用的类紧密耦合(例如,由同一作者编写;属于同一应用程序的一部分;或嵌套类,可能是私人的)。 在这种情况下,可以避免负面影响,因为Second作者知道(或甚至有可能写成) First的内部结构。

这取决于情景,但可能导致很难预测行为。 如果Second在构造函数中与First做任何事情,那么一旦改变了First的构造函数,该行为就可能变得不明确。 额外的构造函数指导还build议您不要在构造函数中调用虚拟或抽象方法(在构造类上),因为它可能导致行为可能难以推理的类似结果。

这个问题的答案取决于FirstSecond之间关系的性质。

想想什么样的对象可能是由另一个对象组成的,它本身是由(或者需要初始化的) Firsttypes的对象组成的。 在这样的情况下,你应该小心使用循环创build对象图。

尽pipe如此,在对象图中还是有很多合法的情况需要进行循环。 如果First依赖于Second的状态来执行初始化,那么你应该保持原来的方法,这通常是可以的。 如果Second依赖于First的状态来执行自己的初始化,那么你应该重新排列这个构造函数:

  public First() { // Doing some other initialization stuff, _second = new Second(this); } 

如果前面的两个陈述都是真的( Second取决于第一个状态, First First取决于Second状态),那么你几乎可以肯定会重新devise你的devise,并且更准确地计算出FirstSecond关系的性质。 (也许应该有一些对象Third ,既包含第一个也包含Second ,后两者之间的关系应由Third仲裁)。