2

Why does the compiler give an error about this being unavailable that when initializing an instance variable when defining it?

Keyword 'this' is not available in the current context

The constructor initialization is fine. I understand the scenario here - I just wanted to find the reason (via MSDN or otherwise) why "this" is available to a constructor but not directly if initializing the member variable.

Here is a simple abstraction of the error I'm seeing.

public class ClassA
{
    // Gets compiler error that "this" unavailable
    protected ClassB _a1 = new ClassB(this);

    // Works fine
    protected ClassB _a2;
    public ClassA() { _a2 = new ClassB(this); }
}

public class ClassB
{
    public ClassA A { get; private set; }
    public ClassB(ClassA a) { this.A = a; } 
}

I was hoping to keep my initialization next to the assignment since the above example is an abstraction of my code where I am defining Lazy valueFactory delegates for 10-15 member variables in which the delegates need the data context passed as a parameter to the constructor. With 15 member variables, I'd prefer to keep the assignment next to the definition on a single line instead of having 15 lines of definitions and then another 15 lines in the constructor initializing each one.

This is basically what I had to do in my actual code:

public class MyContext
{
    public ProgramService Programs { get { return _programs.Value; } }
    protected Lazy<ProgramService> _programs;

    public MyContext()
    {
        _programs = new Lazy<ProgramService>(() => new ProgramService(this));
    }
}
Jason W
  • 13,026
  • 3
  • 31
  • 62
  • Is it possible the fields to be initialzed before the constructor gets hit – Royal Bg Jul 28 '15 at 14:34
  • 1
    Here's pretty good answer to your question: http://stackoverflow.com/questions/6125247/why-cant-you-use-this-in-member-initializers – michalk Jul 28 '15 at 14:40
  • Although now that I look at it, the question michalk references is probably more accurate. – Powerlord Jul 28 '15 at 14:41
  • @michalk - Looks like Jon Skeet's answer is exactly why - no reason specified in annotated C# specs, just that it is not possible. – Jason W Jul 28 '15 at 15:44

1 Answers1

4

Basically to keep you from relying on the order (textual) and possibly non-usable state of the class under construction. I think the overriding principle at stake is that if it's "complicated" logic, then you should be using a constructor.

Eg., doing this:

class A {
   private int x = 1;
   private int y = this.x + 1;
}

would lead to different results than:

class A {
   private int y = this.x + 1;
   private int x = 1;
}

which is a little unexpected. To side-step the issue, but still allow the convenience of inline init - disallowing this makes the ordering not matter.

In your actual code, you could possibly do a lazy check to keep things located together:

public ProgramService Programs { 
   get { 
      if (_programs == null) _programs = new ProgramService(this);
      return _programs.Value; 
   } 
}
// change this to private to keep subclasses from accessing a possibly null reference
private Lazy<ProgramService> _programs;
Mark Brackett
  • 84,552
  • 17
  • 108
  • 152
  • You imply that the initialization is done top-down. Is that actually defined somewhere? – Ralf Jul 28 '15 at 14:34
  • 1
    @Ralf - link added to spec ("variable initializers are executed in the textual order in which they appear in the class declaration") – Mark Brackett Jul 28 '15 at 14:36
  • One additional thing of note: instance fields are initialized as soon as any of the constructors are called... which implies it happens before any calls to a super class's constructor. – Powerlord Jul 28 '15 at 14:40