Sending reference of object before its construction

C#ReferenceConstructor

C# Problem Overview


I have seen the following code in one of our applications:

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

}

public class Second
{
    public Second(First f)
    {
    }
}

In the First() constructor, isn't it bad that we are sending a reference of class First() before it is fully constructed? I am thinking that the object is only fully constructed once the control logic leaves the constructor.

Or is this okay?

C# Solutions


Solution 1 - C#

> My question is, in the First() constructor, isnt it bad that we are sending a reference of class First() BEFORE it is fully constructed?

Somewhat. It can be a problem, certainly.

If the Second constructor just holds onto a reference for later use, that's not too bad. If, on the other hand, the Second constructor calls back into First:

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

... and the state hasn't been set up yet, then that would of course be a Very Bad Thing. If you call a virtual method on First then it could be even worse - you could end up calling into some code which hasn't even had a chance to run any of its constructor body yet (although its variable initializers will have been executed).

In particular, readonly fields may be seen first with one value and then later with another...

I blogged about this a while ago, which may provide some more information.

Of course, without doing this sort of thing, it's pretty hard to create two mutually-referential immutable objects...

Solution 2 - C#

If you encounter this pattern, you might check if it can be refactored into this instead:

public class First()
{
      private Second _second;

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

      private class Second
      {
          public Second(First f)
          {
          }
      }
}

Passing the dependency into the constructor implies a kind of tight coupling between the two classes, as First has to trust that Second knows what it is doing and won't try to rely on the uninitialized state of First. This kind of strong coupling is more appropriate when Second is a private nested subclass (and hence a clear implementation detail) or possibly when it's an internal class.

Solution 3 - C#

The answer is, it depends. Generally, though, this would be considered a bad idea due to the potential consequences.

More specifically, though, as long as Second doesn't actually use anything from First before it is constructed, then you should be okay. If you can't guarantee that somehow though, you could definitely run into problems.

Solution 4 - C#

Yes it is somewhat bad. It is possible to do things to the fields of First before it is fully initialized, which would cause undesired or undefined behavior.

The same thing happens when you call a virtual method from your constructor.

Solution 5 - C#

In contrast to e.g. C++, CLR has no notion of fully constructed or incompletely constructed objects. As soon as the memory allocator returns a zeroed object and before the constructor runs, it is ready to use (from the CLR's point of view). It has its final type, calls to virtual methods invoke the most derived override etc. You can use this in constructor's body, call virtual methods etc. This may indeed cause problems with order of initialization, but there is nothing in CLR to prevent them.

Solution 6 - C#

It is true that this might lead to problems as you described. Therefore, it is generally adviseable to run commands such as _second = new Second(this); only after the other initialization stuff implied by your comment.

Quite sometimes, this pattern is the only solution for storing reciprocal references between two objects. In many cases, however, this occurs in a way that the class receiving the possibly-not-fully-initialized instance is tightly coupled to the referenced class (e.g. written by the same author; part of the same application; or a nested class, possibly private). In such cases, negative effects can be avoided as the author of Second knows (or has possibly even written) the internals of First.

Solution 7 - C#

It depends on scenario, but it could lead to difficult to predict behavior. If Second does anything with First in the constructor, that behavior may become ill-defined once you alter the constructor of First. Additional constructor guidance also suggests that you shouldn't call virtual or abstract methods (on the constructed class) in a constructor, because it can lead to similar consequences where behavior may be difficult to reason about.

Solution 8 - C#

The answer to this question depends on the nature of the relationship between First and Second.

Think about what sort of object might be composed of another object, which is itself composed of (or requires for its initialization) the object of type First. In situations like this, you should be wary of creating object graphs with cycles.

Nevertheless, there are plenty of legitimate situations in which a cycle should occur in an object graph. If First relies on the state of Second to perform its initialization, then you should keep the method as is and that is generally okay. If Second relies on the state of First to perform its own initialization, then you should probably rearrange the constructor as such:

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

If both of the preceding statements are true (Second depends on the state of First, and First depends on the state of Second), then you should almost certainly revisit your design, and figure out more precisely the nature of the relationship between First and Second. (Maybe there should be some object Third that contains a reference to both First and Second, and the relationship between the latter two should be arbitrated by Third.)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1202434View Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#Dan BryantView Answer on Stackoverflow
Solution 3 - C#Justin NiessnerView Answer on Stackoverflow
Solution 4 - C#Daniel A.A. PelsmaekerView Answer on Stackoverflow
Solution 5 - C#Anton TykhyyView Answer on Stackoverflow
Solution 6 - C#O. R. MapperView Answer on Stackoverflow
Solution 7 - C#eulerfxView Answer on Stackoverflow
Solution 8 - C#Michael GraczykView Answer on Stackoverflow