none
Call function of derived class RRS feed

  • Question

  • It's probably a very simple question, but I am not sure how to design this class structure correctly. I have an abstract base class and two derived classes with different extended methods. In my application a List of Animals is stored. At some point in my code I select from this list explicitly one instance of the derived classes (animals[1]) which still has the type of the base class. So I get an error because the method of the derived class it not found.

    What is a best practise to model this? I know I could define the fly() method already in Animal, but is this a correct way to do that?

    public abstract class Animal
    {
        public Name string { get; set; }
    }
    
    public class Bird : Animal
    {
        public void fly(){Console.Write("I can fly");}
    }
    
    public class Dog : Animal
    {
    
    }
    
    List<Animal> animals = new List<Animal>;
    animals.Add(new Dog{Name = "Max"});
    animals.Add(new Bird{Name = "Peter"});
    
    animals[1].fly(); // Error
    (Bird) animals[1].fly(); // Error
    Wednesday, August 7, 2019 11:52 AM

Answers

  • I don't think base/derived is the best choice here. A base class is for providing a base implementation of some functionality while allowing a derived class to alter it. Calculating area of a shape is a good example. All shapes have an area (theoretically) so Area would be a base class member. If a member doesn't apply to all "shapes" then it doesn't go in the base class. Derived types can have their own members but now you have to know that the instance you're working with has that functionality before it can be used.

    //May or may not be a bird
    var bird = animals[1] as Bird;
    bird?.Fly();
    
    //Or for newer C# versions
    if (animals[1] is Bird bird)
    {
       bird.Fly();
    };

    But this code isn't really taking advantage of base/derived functionality anyway so `animals` could just as well be an array of objects and the code would be the same.

    When you need to indicate functionality then a better option is to use an interface. An interface specifies that a type provides/implements some functionality beyond what the underlying type might have.

    public interface ICanFly
    {
       void Fly (); 
    }
    
    public Bird : Animal, ICanFly
    {
       public void Fly () { }
    }

    Now to use it the code is similar but you are no longer limited to "animals". For example a plane can also fly.

    if (animals[1] as ICanFly fly)
    {
       fly.Fly();
    };
    The bigger benefit here is that types can now implement a variety of features without having to manage an ever growing hierarchy. For example you might want to identify animals that can swim. What happens when you have an animal that can fly and swim? With base/derived you'd have to create a combined hierarchy and C# only supports single inheritance. However a single type can implement any # of interfaces.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by HansHupe Wednesday, August 7, 2019 2:40 PM
    Wednesday, August 7, 2019 1:50 PM
    Moderator

All replies

  • You actually have a list of base class which is Animal and it is unaware of the fact that your derived class have what data members or methods. You should not be doing like that. but if it's just for experimenting or learning purpose then you need to check before invoking that if it is Bird then call fly method  like:

    List<Animal> animals = new List<Animal>;
    animals.Add(new Dog{Name = "Max"});
    animals.Add(new Bird{Name = "Peter"});
    
    var animal = animals[1];
    if(animal is Bird) // check if it is Bird
    {
    
       Bird b = animal as Bird; // hold reference as of Bird class
       b.fly(); // now we can call fly on the bird object
    
    }


    [If a post helps to resolve your issue, please click the "Mark as Answer" of that post or click Answered"Vote as helpful" button of that post. By marking a post as Answered or Helpful, you help others find the answer faster. ]


    Blog | LinkedIn | Stack Overflow | Facebook
    profile for Ehsan Sajjad on Stack Exchange, a network of free, community-driven Q&A sites





    Wednesday, August 7, 2019 1:19 PM
  • Technically I understand the errors. But what would be a better approach to design the classes according to OOP?

    Note that in my real example the "Bird" class includes many more members which are not needed in "Dog". Still it seems correct to have a common base class as they share some common things as well.

    Wednesday, August 7, 2019 1:26 PM
  • You need to be clear about what should you make common. The name makes senses to be defined in base class but of course the fly method should only be in the Bird class. Holding bird and dog in Animal list does not sounds promising if you need to invoke members of Dog and Bird while iterating on it. Better would be to keep them in separate collections.

    [If a post helps to resolve your issue, please click the "Mark as Answer" of that post or click Answered "Vote as helpful" button of that post. By marking a post as Answered or Helpful, you help others find the answer faster. ]


    Blog | LinkedIn | Stack Overflow | Facebook
    profile for Ehsan Sajjad on Stack Exchange, a network of free, community-driven Q&A sites

    Wednesday, August 7, 2019 1:33 PM
  • The reason why i have them in the same collection is because those "Animal" objects can be linked together in a graph structure by the user. For the graph collection I only need the generic "Animal" type.
    Wednesday, August 7, 2019 1:38 PM
  • I don't think base/derived is the best choice here. A base class is for providing a base implementation of some functionality while allowing a derived class to alter it. Calculating area of a shape is a good example. All shapes have an area (theoretically) so Area would be a base class member. If a member doesn't apply to all "shapes" then it doesn't go in the base class. Derived types can have their own members but now you have to know that the instance you're working with has that functionality before it can be used.

    //May or may not be a bird
    var bird = animals[1] as Bird;
    bird?.Fly();
    
    //Or for newer C# versions
    if (animals[1] is Bird bird)
    {
       bird.Fly();
    };

    But this code isn't really taking advantage of base/derived functionality anyway so `animals` could just as well be an array of objects and the code would be the same.

    When you need to indicate functionality then a better option is to use an interface. An interface specifies that a type provides/implements some functionality beyond what the underlying type might have.

    public interface ICanFly
    {
       void Fly (); 
    }
    
    public Bird : Animal, ICanFly
    {
       public void Fly () { }
    }

    Now to use it the code is similar but you are no longer limited to "animals". For example a plane can also fly.

    if (animals[1] as ICanFly fly)
    {
       fly.Fly();
    };
    The bigger benefit here is that types can now implement a variety of features without having to manage an ever growing hierarchy. For example you might want to identify animals that can swim. What happens when you have an animal that can fly and swim? With base/derived you'd have to create a combined hierarchy and C# only supports single inheritance. However a single type can implement any # of interfaces.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by HansHupe Wednesday, August 7, 2019 2:40 PM
    Wednesday, August 7, 2019 1:50 PM
    Moderator
  • Think of it this way.  Let's say you have an Animal.  If you need to be able to ask the question, "does this Animal fly?", then it should be clear that the Animal class itself has to have a "fly" method.  You could have the default answer be "no" in the base class, so you only have to override it in those subclasses that DO fly.

    That's the way you design your interface.  You ask yourself, "what information do I need to know about these generic object?"  That determines the interface.  A subclass might have more detailed information, but you would only access that after you knew it was a Bird.


    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    Wednesday, August 7, 2019 5:18 PM