3

I have a class structure like this where an abstract class A holds an interface IB and a concrete class C in two variables. Which classes get assigned to that interface variable depends on the actual subclass A1, A2 that inherits the abstract class, and need that class C that gets instantiated in the parent class constructor. So what I came up first was this:

abstract class A
{
    protected readonly IB _b;
    protected readonly C _c;

    protected A()
    {
        _c = new C();
    }
}

class B1 : IB
{
   public B1(C c) {}
}

class B2 : IB
{
   public B2(C c) {}
}

class A1 : A
{
    public A1() : base()
    {
        _b = new B1(_c);
    }
}

class A2 : A
{
    public A2() : base()
    {
        _b = new B2(_c);
    }
}

The assignment _b = new B1(_c); doesn´t work because its protected. I also can´t pass the class Bx down to the base constructor, because I need that to be executed before I can instantiate Bx. How else can I achieve my desired outcome?

downforme
  • 371
  • 2
  • 15
  • `The assignment _b = new B1(_c); doesn't work because its protected` that's not the problem is it - the peoblem is its `readonly` state? Also, isn't the problem that `C` doesnt get instantiated until _after_ `B1` – Jamiec Sep 15 '21 at 12:29
  • no that´s not the issue. Base constructor gets called first, so C is already instantiated when derived constuctor body gets executed. _b = new B1(_c); won´t compile (Error CS0191 A readonly field cannot be assigned to ...) – downforme Sep 15 '21 at 12:34

2 Answers2

2

A readonly field can only be instantiated in the same class, not in a subclass. Thus, A's constructor needs to set _b.

However, you can pass the knowledge of how to set _b from the subclass to the abstract base class by passing a lambda expression:

Constructor for A:

protected A(Func<C, IB> bFactory)
{
    _c = new C();
    _b = bFactory(_c);
}

Constructor for A1:

public A1() : base(c => new B1(c)) { }
Heinzi
  • 167,459
  • 57
  • 363
  • 519
2

_b = ... doesn't work because readonly fields can only be initialised in the constructor of the class that declares it, not in a subclass. It's not because _b is protected. The documentation says:

In a field declaration, readonly indicates that assignment to the field can only occur as part of the declaration or in a constructor in the same class.

One way to work around this is to have the superclass take in a Func<C, B1>, and initialise _b there.

protected A(Func<C, IB> bCreator)
{
    _c = new C();
    _b = bCreator(_c);
}

then:

class A1 : A
{
    public A1() : base(c => new B1(c))
    {
        
    }
}

class A2 : A
{
    public A2() : base(c => new B2(c))
    {
        
    }
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313