0

QUESTION

How to make base constructor neccessary to call in derrived class. When you don't implement abstract method IDE shows error message and you understand tha you should implement this:) I want to have some message when programmer doesn't call base(). Moreover if hierarchy is deep I need to call the first base class constructor. Maybe there are some workarounds that will make this call automatically or some solutions to say programmer that he need to make this call. As you can see this set method SetShapeWorker called in abstract class Shape constructor. I need to make neccessary to call this constructor in all derived classes.

Description

I have the shape. I decided to implement some alghorithms with Command pattern, so Shape is receiver. Also in this case it is an invoker, because inside some methods I need to call some methods from ShapeWorker. Here it is SizeDialog. Look at constructor in Shape, I have shapeWorker variable that methods I use inside some Shape methods - look at default implementation of SetSizeWorker().

I want to make neccessary to initialize ShapeWorker with concrete receiver - Square or Rectangle. So I declare astract SetShapeWorker and if another programmer or maybe I after some weeks add Triangle, the IDE will show message that I need tom implement protected SetShapeWorker and programmer should understand that he need to write something like this:

class Triangle : Shape {
    //adding the triangle shape worker
    public override SetShapeWorker() {
        shapeWorker = new ShapeTriangleWorker();
        shapeWorker.SetReceiver(this);
    }
}

and after this all command invoker methods should work. As you can see this set method called in abstract class Shape constructor. So I want to make neccessary to call this constructor in all derived classes.

And the reason why I make this initialization: the use of casts (receiver as Rectangle).SetSize(a,b), (receiver as Square).SetSize(a). Uncle Bob in one of the videos says that to make these casts I should be sure that cast is valid, I agree with him:) I try to do it with is private for user interface SetShapeWorker where programmer should set the concrete receiver.

THE CODE

abstract class Shape {
    private ShapeWorker shapeWorker;    
    public Shape() {
        SetShapeWorker();
    }
    private bool isSet = false;
    public IsSet {
        get {
            return isSet;
        }
    }
    public abstract int Square();
    protected abstract void SetShapeWorker();
    public void SetSizeWorker() {
        if(shapeWorker != null) {
            shapeWorker.SizeDialog();
        }
    }
}
class Square : Shape {
    private int a;
    public Square() {

    }
    public SetSize(int a) {
        this.a = a;
        isSet = true;
    }
    public override int Square() {
        return a*a;
    }
    protected override SetShapeWorker() {
        shapeWorker = new ShapeSquareWorker();
        shapeWorker.SetReceiver(this);    
    }
}
class Rectangle : Shape {
    private int a, b;
    private Rectangle() : base() {
    }
    public SetSize(int a, int b) {
        this.a = a;
        this.b = b;
        isSet = true;
    }
    public override int Square() {
        return a*b;
    }
    protected override SetShapeWorker() {
        shapeWorker = new ShapeRectangleWorker();
        shapeWorker.SetReceiver(this);    
    }
}

abstract class ShapeWorker 
{
    public Shape receiver;
    public abstract void SetReceiver(Shape receiver) {
        this.receiver = receiver;
    }
    public abstract void SizeDialog();
    public abstract int StrangeCountSquare();
    public int getDeltaSquare() {
        if(receiver.IsSet == false) {
            SizeDialog();            
            return StrangeCountSquare();
        }
    }
}
class ShapeSquareWorker {
    public override void SizeDialog() {
        int a;
        Console.WriteLine("Enter the a: ");
        Int32.TryParse(Console.ReadLine(), out a);
        (receiver as Square).SetSize(a);
    }

    public override int StrangeCountSquare() {
        return receiver.Square() + 10;
    }
}
class ShapeRectangleWorker {
    public override void SizeDialog() {
        int a, b;
        Console.WriteLine("Enter the a: ");
        Int32.TryParse(Console.ReadLine(), out a);
        Console.WriteLine("Enter the b: ");
        Int32.TryParse(Console.ReadLine(), out b);
        (receiver as Rectangle).SetSize(a,b);
    }

    public override int StrangeCountSquare() {
        return receiver.Square() + 20;        
    }
}

BTW Now I'm learning SOLID and patterns, where is the best suit place to discuss in stackoverflow? F.e. if I want to discuss about usefull ways mixing some patterns. Answer please in comment.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Nikita
  • 1,019
  • 2
  • 15
  • 39
  • Probably `ShapeSquareWorker` and `ShapeRectangleWorker` should be derivatives of `ShapeWorker`... – JHBonarius Mar 24 '17 at 09:20
  • I'm not sure I follow, but when you have a single constructor in the base class, it will always be called, regardless of whether the developer will make it explicit or no. – BartoszKP Mar 24 '17 at 09:20
  • What's wrong with your code? SetShapeWorker will be called because default constructor will be called in a base class. – MistyK Mar 24 '17 at 09:24
  • It isn't optional to call constructors in base classes in .NET. You can specify which one to call, but if you don't, the default parameterless constructor will be called. If you don't have that, and don't specify which other constructor to call instead, you will get a compiler error for your derived class. What more do you want? Do you want to require the programmer to explicitly write out the call to `base()`, even if the compiler will inject it for you? – Lasse V. Karlsen Mar 24 '17 at 09:27
  • In short, it already **is** necessary to call a constructor in the base class, it is enforced by the compiler. However, the compiler might "help you" by injecting the call to the parameterless constructor in the base class if you don't explicitly specify which constructor in the base class to call. – Lasse V. Karlsen Mar 24 '17 at 09:28
  • 1
    I just notices you have an [Anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern) in your design: a [Circular Dependency](https://en.wikipedia.org/wiki/Circular_dependency). `Shape` is referrering to `ShapeWorker`, and `ShapeWorker` is referring to `Shape`. This is not proper OO. – JHBonarius Mar 24 '17 at 09:40
  • @J.H.Bonarius Thanks, I missed this. – Nikita Mar 24 '17 at 09:43

1 Answers1

3

I think you misunderstand how things work if you only have a single default constructor in a base class. It will always be called when a derived class is created, even if it's not called explicitly:

using System;

namespace ConsoleApp2
{
    public abstract class Base
    {
        public Base()
        {
            Console.WriteLine("Base constructor called.");
        }
    }

    public class Derived : Base
    {
    }

    class Program
    {
        static void Main()
        {
            Derived d = new Derived(); // Prints "Base constructor called."
        }
    }
}

Also, if you replace the base class' default constructor with a non-default constructor, you'll get a compile error if you don't call it from a derived class.

If you have both a default AND a non-default constructor in the base class, then if the non-default constructor is not called by a derived class constructor, the base class default constructor will be automatically called:

using System;

namespace ConsoleApp2
{
    public abstract class Base
    {
        public Base()
        {
            Console.WriteLine("Default base constructor called.");
            Value = -1;
        }

        public Base(int value)
        {
            Console.WriteLine("Non-default base constructor called.");
            Value = value;
        }

        public int Value;
    }

    public class Derived : Base
    {
    }

    class Program
    {
        static void Main()
        {
            Derived d = new Derived(); // Prints "Default base constructor called."
        }
    }
}

If you want to have a default constructor for the base class that is used for its own implementation purposes, but force derived classes to use the non-default constructor, then you can make the base class default constructor private.

If you do that, you'll get a compile error if any derived class constructor fails to call the base class non-default constructor.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276