Friday, April 15, 2011

Constructor chaining with "this"

Why does the first constructor in ClassA cause the compiler error 'cannot use "this" in member intializer'?

... or how can i get this to work?

Thanks

public sealed class ClassA : IMethodA
{
    private readonly IMethodA _methodA;

    public ClassA():this(this)
    {}

    public ClassA(IMethodA methodA)
    {
        _methodA = methodA;
    }

    public void Run(int i)
    {
        _methodA.MethodA(i);
    }

    public void MethodA(int i)
    {
        Console.WriteLine(i.ToString());
    }
}

public interface IMethodA
{
    void MethodA(int i);
}
From stackoverflow
  • You are allowed to use the this(...) syntax to invoke another constructor at the same level - however, you cannot use this (the current instance) in this context.

    The easiest option here is to duplicate the assignment code (_methodA = methodA).

    Another option might be null-coalescing:

    public ClassA():this(null)
    {}
    
    public ClassA(IMethodA methodA) 
    { // defaults to "this" if null
        _methodA = methodA ?? this;
    }
    
    Bobby Cannon : @Marc Gravell: beat me to it...
  • This is called out in section 10.11.1 of the C# spec

    An instance constructor initializer cannot access the instance being created. Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as is it a compile-time error for an argument expression to reference any instance member through a simple-name.

    There is no way to get this to work with an instance constructor because this cannot be accessed. What you could do is make the constructor private, create an initialization method and a static constructor.

    public sealed class ClassA : IMethodA {    
      private ClassA() { }
      private void Initialize(IMethodA param) { ... }
      public static ClassA Create() {
        var v1 = new ClassA();
        v1.Initialize(v1);
        return v1;
      }
      public static ClassA Create(IMethodA param) {
        var v1 = new ClassA();
        v1.Initialize(param);
        return v1;
      }
    }
    
  • You're trying to pass the object before it is constructed. Although the compiler could do something sensible in this case, in general that won't work.

    Your actual example works if you just do this:

       public ClassA()
      {
        _methodA = this; 
      }
    

    But you probably want to share more logic, so just use a function.

      public ClassA()
      {
        SetStuff(); 
        _methodA = this; 
      }
    
      public ClassA(IMethodA methodA)
      {
        SetStuff(); 
        _methodA = methodA;
      }
    
    Marc Gravell : Note that you can still use SetStuff with readonly fields... *if* you use a `ref` / `out` argument. Whether it is worth it depends on the scenario.
  • You can't use the this keyword when chaining constructors essentially because this refers to an object that hasn't been instantiated yet (creation of the object doesn't begin until some (the top-level or base) constructor block has been entered). Moreover, why exactly would you want to do this? It seems rather pointless when you have access to the this keyword everywhere.

    I recommend simply using independent constructors as such:

    public sealed class ClassA : IMethodA
    {
        private readonly IMethodA _methodA;
    
        public ClassA()
        {
            _methodA = this;
        }
    
        public ClassA(IMethodA methodA)
        {
            _methodA = methodA;
        }
    }
    

    Perhaps I misunderstand what you're trying to do, but hopefully that will solve the issue for you.

0 comments:

Post a Comment

Note: Only a member of this blog may post a comment.