2

A simplified scenario. Three clases, GrandParent, Parent and Child. What I want to do is to make use of the GrandParent and Parent constructor to initalize a Child instance.

class GrandParent ()
{
    public int A {get; protected set;}
    public int B {get; protected set;}

    public GrandParent (int a, int b);
    {
        A = a;
        B = b;
    }
}

class Parent : GrandParent ()
{
    public Parent () {}
}

Child Class, were the problem occurs.

class Child : Parent ()
{
    public int C {get; protected set}

    public Child (int c) // here I want it to also call the GrandParent (int a, int b)
                         // before calling Child (int c)
    {
        C = c;
    }
}

Child = new Child (1,2,3);

What I want is that the variables a,b and c to get 1,2 respectively 3 as values. I know I can solve it by simply adding A = a and B = b to the Child constructor.

Is this possible? If so how?

I begun looking at base () however it looked like it was only able to access the Parent class and not the GrandParent.

Thanks in advance.

PS. If it has been asked before, I apologize in advance, I did not find anything.

Quick edit: I am trying to make the solution as easy as possible to work with, to develop further.

4
  • 2
    For this to even compile, wouldn't Parent need a constructor that takes two arguments? Commented Jun 5, 2014 at 17:58
  • This does not currently compile, what I am using right now is A=a and B=b as well as taking in its arguments into Child. My question regards if it is possible without? Commented Jun 5, 2014 at 18:01
  • There's also a syntax error, as there should be no parentheses () after the name of the class. Commented Jun 5, 2014 at 18:09
  • The moral of the story is, we should avoid using Parent, Grandparent and Child as example names when explaining inheritance. A child is not a type of parent, a parent is not a type of grandparent. The names don't adequately explain the hypernym/homonym relationship, and so it isn't as immediately obvious that "thing that can be created without an A or B being set" cannot be a type of "thing that can only be created with an A or B being set". Commented Jun 5, 2014 at 18:30

4 Answers 4

5

You can only invoke the constructors to the direct superclass. The direct superclass needs to expose the options that you need. It can safely do this with a protected constructor.

Sign up to request clarification or add additional context in comments.

1 Comment

+1 Was verifying this on MSDN when you posted. base always goes to the direct parent.
2

C# good practise assumes that you call base class constructor in every child. That said, you'll call base(a,b) from parent and base(a,b) from child, which will invoke grandparent ctor. That grants you incapsulation. Here is you code snippet rewriten:

class GrandParent ()
{
    public int A {get; protected set;}
    public int B {get; protected set;}

    public GrandParent (int a, int b);
    {
        A = a;
        B = b;
    }
}

class Parent : GrandParent ()
{
    public Parent (int a, int b):base(a,b) {}
}
Child Class, were the problem occurs.

class Child : Parent ()
{
    public int C {get; protected set}

    public Child (int a, int b, int c):base(a,b)
    {
        C = c;
    }
}

Comments

1

You need to pass the variables through each constructor to its base constructor:

class Child : Parent ()
{
    public int C {get; protected set}

    public Child (int a, int b, int c) : base(a,b)
                         // before calling Child (int c)
    {
        C = c;
    }
}

Child = new Child (1,2,3);

class GrandParent ()
{
    public int A {get; protected set;}
    public int B {get; protected set;}

    public GrandParent (int a, int b);
    {
        A = a;
        B = b;
    }
}

class Parent : GrandParent ()
{
    public Parent (int a, int b) : base(a,b) {}
}

1 Comment

I suspected as much, making it cleaner to some extent. Not as clean as I wanted it to be.
1

parent and child is an analogy of limited applicability, because a derived class is not a "child" of the base as a person's or animal's child is a child of them, or even as a subdirectory is of a directory or dependent node in a tree (GUI or data-structure).

The relationship is actually that of hypernym to hyponym. Sadly, hypernym isn't as well-known a word as parent, so people often use the less-accurate parent in explaining inheritance rather than hypernym.

Now, let's consider the first class declaration:

class GrandParent
{
    public int A {get; protected set;}
    public int B {get; protected set;}

    public GrandParent (int a, int b)
    {
        A = a;
        B = b;
    }
}

This is exactly as in the question, except the erroneous () on the first line and ; on the sixth have been removed.

It has a single constructor, that takes two integers.

This means either:

  1. It makes no logical sense for a GrandParent to ever be created, without two integers being passed.
  2. There's a bug in how this was defined.
  3. Okay, maybe not a bug, but certainly a tad lazy.

When we come to the declaration of Parent:

class Parent : GrandParent
{
    public Parent () {}
}

Here I've fixed the syntax errors, but nothing else.

Since there is no explicit use of base in the constructor there is an implied call to a parameterless base(). That is, the above is exactly the same as:

class Parent : GrandParent
{
    public Parent()
        : base()
    {
    }
}

Any call to new Parent() will through the base() call new GrandParent(). Since there is no parameterless constructor on GrandParent() this can't compile.

And here the language rules are helping us:

  1. We said that it makes no sense for a GrandParent to be created without two integers being used.
  2. We said that a Parent is a type of GrandParent.
  3. We said that a Parent is only ever created without those two integers being set.

We said three things are true that cannot all be true. The language rules are correct to point out our mistake.

To fix this we need to either:

  1. Allow a GrandParent to be created without those two integers being used in its construction.
  2. Force Parents to always have two integers being used in their construction, which are then in turn used in turn with the base constructor.
  3. Have the parameterless constructor on Parent call base(a, b) with some set values.
  4. Do a mixture of the above. E.g. we could have both set values called if the parameterless constructor was called, and also another constructor that passes arguments to the base constructor.

The 3rd above would look something like:

class Parent : GrandParent
{
    public Parent()
        : base(42, -343213)
    {
    }
}

Or whatever, as long as those numbers come from somewhere (obviously in real code they would relate to something more sensible than my random typing).

Now we have a Parent that actually makes logical sense, and the rest can follow.

1 Comment

Thank you. I think I now understand it. Sorry for the small errors, I rewrote the code to minimize it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.