Search code examples
c#.netradix

Getting methods from base class?


I am brand new to base classes so please correct me if I am wrong or I should be doing this another way, but I have coded a simple code snippet below that explains what I want to do.

class A : B
{
    private int _classId;

    public A (int id) : base(id)
    {
        _classId = id;
    }

    public string GetString()
    {
        return "This will eventually be an important string.";
    }
}

class B
{
    private int _classId;

    public B (int id)
    {
        // here I want to be able to get access to class A's methods
        // such as GetString() ...
    }

    public void SomeMethod()
    {

    }
}

So what I want to do is allow B to get A's methods.

Reason for doing this? I don't want to populate the class A because B is going to have a lot of methods so it would be cleaner to keep them in B then just get them from A in other code. Am I doing this wrong? is this not what base classes do? I simply just want to be able to still do this:

A.SomeMethodInB();

Without populating class A with junk and making it cleaner by storing it in class B


Solution

  • Let me summarize your question:

    • You want a method that is only implemented in a descendant class (A)
    • You want this method to be available to both the base class (B) and the descendant class (A)
    • You want the base class to be able to call the descendant class' implementation of the method

    This is the original definition of polymorphism (lately people have been using the term much more generally) and is bread-and-butter when it comes to OOP. It is also the hardest area of OOP to grasp for newcomers.

    Write a base class with a virtual method

    Let us start with the base class. Notice I have changed your example slightly, for a reason I will explain in a moment.

    class B
    {
        virtual protected string GetString()
        {
            return "I am a B";
        }
    
        public void DoSomethingWithGetString()
        {
            Console.WriteLine(GetString());
        }
    }
    

    In this example, DoSomethingWithGetString calls GetString. Note that GetString is virtual. This tells the compiler that we don't want to decide where the implementation of GetString is until runtime. This means it can't be bound at compile time: instead, its location is identified by a runtime Virtual Method Table that is set up when the object is created. If this code is running as part of a instance of class B, then it will call class B's implementation of GetString. But if this code was inherited, and is actually being called by some other object that descends from class B, it will call that class' version of GetString. This is called late binding.

    Write the descendant class

    So now let's write class A:

    class A : B
    {
        protected override GetString()
        {
            return "I am an A.";
        }
    }
    

    Because A descends from B, we don't need to implement anything else. If we call A.DoSomethingWithGetString it'll actually call B's implementation because it is inherited.

    Execute it

    If you run this code

    var a = new A();
    a.DoSomethingWithGetString();  //Output = "I am an A"
    

    ...the following will happen:

    1. An instance of A, which descends from B, is created
    2. The VMT of A is configured so that calls to GetString are sent to A.GetString.
    3. You call A.DoSomethingWithGetString
    4. This call is directed to B's code base, because A doesn't implement it.
    5. The code base in B detects that GetString is a virtual method, and calls it using the VMT.
    6. The VMT points to A's implementation of GetString.
    7. A.GetString is executed.

    Thus you have an situation where code in class B is calling code in class A, which is what you asked for.

    You can also run it this way:

    B b = new A();
    b.DoSomethingWithGetString(); //Output = "I am an A"
    

    Thus you have a pointer to a B that says it's an A, because it is using a VMT set up for class A.

    But I don't want any implementation in class B

    Now, let's say you don't want class B to have any implementation at all for GetString, but you still want to be able to call the implementation in A. There is a way to do this using an abstract virtual method. Simply add the abstract keyword and remove the implementation:

    abstract class B
    {
        abstract protected string GetString(); //Notice no implementation
    
        public void DoSomethingWithGetString()
        {
            Console.WriteLine(GetString());
        }
    }
    

    Of course, now you can't use class B by itself, because it requires an implementation of GetString that will only be present in a descendant class. So you can no longer create an instance of B directly; you have to subtype, and use the subtype.

    Can't use constructor

    Now, you'll notice in my example I removed your constructor calls. There is a reason for this. A constructor should not ever call a method that is defined on a descendant class. The reason: during construction, constructors are called up and down the inheritance hierarchy, starting with the base class and moving down the descendant chain. That means, when you call B's constructor, A's constructor has not been called yet. if A's constructor hasn't run yet, we can't be sure that A has been properly set up yet, and therefore it is a really bad idea to call any of its methods.

    There are some workarounds for this, but that is a topic for a different question.