23

I have the following code class:

public class Foo
{
    public Nested Bar { get; } = new Nested(this);

    public class Nested
    {
        public Nested(Foo foo)
        {
            foo.DoSomething();
        }
    }

    private void DoSomething()
    {

    }
}

However, I get this compile error:

Keyword 'this' is not available in the current context

I can fix it by simply not using Auto-Property Initializer, and explicitly move it into a constructor instead:

public Nested Bar { get; }

public Foo()
{
    this.Bar = new Nested(this);
}

Why is it so? Isn't Auto-Property Initializer actually translated into constructor code in IL?

Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
Luke Vo
  • 17,859
  • 21
  • 105
  • 181
  • 3
    Because auto-property initializer is translated into backing field with initializer. You cannot reference this in field initializer because field initializer runs before constructor, so there is no constructed object yet to use. – Evk May 15 '17 at 10:35
  • @Evk Isnt it so that when constructor runs then the object is also "not yet constructed"? – Roland Pihlakas May 15 '17 at 20:36
  • An interesting question Roland Pihlakas! The this object must already exist in terms of memory being prepared to access. Otherwise the property wouldn't be there. But nothing else is initialized. One could do the wrong order of init steps in the constructor too but what would be more obvious to the programmer. – Droidum May 16 '17 at 05:59
  • Because otherwise [this happens](http://stackoverflow.com/questions/24990691/will-java-final-variables-have-default-values) – user253751 May 19 '17 at 00:16

2 Answers2

41

Simply: you can't use this in initializers. The idea is to prevent an incomplete object from escaping - Nested(this) could do anything to your object, leading to very confusing and hard to understand bugs. Keep in mind that initializers execute before any constructor that you add. The same thing fails for field initializers too, in exactly the same way:

private Nested _field = new Nested(this);

Essentially, initializers are intended to perform simple initializations - fixing the 98% problem. Anything involving this is more complex, and you'll need to write your own constructor - and take the blame for any timing issues :)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • TIL! I am a bit curious if you don't mind: is there details/documentation about what CAN be in the initializer? Why don't they simply put it in constructor instead (before explicit constructor code)? – Luke Vo May 15 '17 at 10:49
  • 4
    @DatVM that is essentially what *does* happen at the IL level; I very deliberately said "any constructor that you add" - so: *if you add* a constructor, it won't have had time to do anything yet. There's also questions of initialization order: which initializers have run yet? It gets *very* tricky to get right if you don't have the explicit timing of a constructor. The language authors don't like things that add subtle complications, so: this usage is forced to use a constructor – Marc Gravell May 15 '17 at 11:00
  • 1
    @datvm Doing it in the pre-constructor wouldn't prevent something like `private int a = this.b; private int b = this.a;` - while specifically unsolvable cases like that could perhaps be caught, it is a lot simpler to just forbid the use of `this` in that context wholesale. – IllusiveBrian May 15 '17 at 21:12
16

Why is it so? Isn't Auto-Property Initializer actually translated into constructor code in IL?

The rules for automatically implemented property initializers are the same as those for field initializers, for the same reason. Note that property initializers are executed before base class bodies, just like field initializers - so you're still in the context of a "somewhat uninitialized" object; more so than during a constructor body.

So you should imagine that the property is being converted into this:

private readonly Nested bar = new Nested(this); // Invalid

public Nested Bar
{
    get { return bar; }
}

In short, this restriction is to stop you from getting yourself into trouble. If you need to refer to this when initializing a property, just do it manually in a constructor, as per your second example. (It's relatively rare in my experience.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194