Question Get fields in inherited class

bondra

Well-known member
Joined
Oct 24, 2020
Messages
77
Programming Experience
Beginner
I'm trying to set the Length field from the inherited class Bone. But only the fields from the base class Toy appears. Any ideas?

C#:
            Pets[0].Name = "Brooom";
            Pets[0].Toys[2].// Fields from inherited class missing

Creation of a new object of type Cat:
            // Create dummy animals at start
            Pets.Add(new Cat
            {
                Name = "Vroom",
                Age = 3,
                FavFood = "Mice",
                Breed = "Ragdoll",
                IsHungry = Application.ShuffleProbability(),
                Toys =
                    new List<Toy> {
                        new Feather {Color = "Blue", Quality = 10},
                        new Ball {Color = "Black/White", Quality = 10},
                        new Bone {Length = 2, Quality = 10},
                    }
            });



Super class Toy:
    abstract class Toy
    {
        public string Color { get; set; }
        public int Quality { get; set; }

    }

Class: Bone:
    class Bone : Toy
    {
        public int Length { get; set; }

    }

Untitled-2.jpg
 
Firstly, those are properties, not fields.

As for the question, we basically covered this in your previous thread. In that thread, I said this:
If you want to treat it as a Cat, i.e. access those members that are specific to the Cat type, then you need to declare it as type Cat. If you intend to treat it as an Animal, i.e. only access those members common to all types derived from Animal, then declaring it as type Animal is OK and probably preferable
In your code, Toys is a List<Toy>, therefore Toys[2] is type Toy. Why would you expect the compiler to know that that Toy reference will actually refer to a Bone object? That same collection contains a Feather object and a Ball object too, but you're not expecting to see members of those types. The compiler doesn't take into account what type of objects you expect to be there at run time, only what type of objects you tell it definitely will be there. It knows that a Toy reference will have the members of the Toy type but nothing more, so that's all it exposes to you.

If you know that a more specific definitely will be there at run time and you want to work with that type then that's where a cast comes in. You can tell the compiler about the more specific type by casting, e.g.
Explicit Cast Example:
var bone = (Bone) Pets[0].Toys[2];

bone.Length = 1;
or just:
Explicit Cast Example:
((Bone) Pets[0].Toys[2]).Length = 1;
If you don't know for sure that a derived type will be available but you want to work with that type if it is then you can perform a conditional cast, e.g.
Conditional Cast Example:
var bone = Pets[0].Toys[2] as Bone;

if (bone != null)
{
    bone.Length = 1;
}
You should develop a working understanding of how casting works and when it is required ASAP.
 
And that last bit of code becomes leaner with C# 7.0's pattern matching:
C#:
if (Pets[0].Toys[2] is Bone bone)
{
    bone.Length = 1;
}
 
Haven't been aware of the way of using is. only ==. Which at first glance seem to do the same thing.
Also casting is kind of new, but after reading about it and playing around with your examples I think I get the idea.
 
Last edited:
I've tried to implement you examples within a foreach loop and if statements. What improvements can be done over here? Is it repetitive? Is the way to check weather or not a list contains a certain object complicated?

C#:
// Printing out Cats
Console.WriteLine(" Cats: ");

foreach (var animal in Pets)
{
    if (Pets.OfType<Cat>().Any())
    {
        if (animal is Cat cat)
        {
            Console.WriteLine(cat);
        }
    }
    else
    {
        Console.WriteLine(" You have no Cats");
        break;
    }
}

// Printing out Dogs
Console.WriteLine("\n Dogs: ");
foreach (var animal in Pets)
{
    if (Pets.OfType<Dog>().Any())
    {
        if (animal is Dog dog && !(animal is Puppy))
        {
            Console.WriteLine(dog);
        }
    }
    else
    {
        Console.WriteLine(" You have no dogs");
        break;
    }
}

// Printing out Puppies
Console.WriteLine("\n Dogs: ");
foreach (var animal in Pets)
{
    if (Pets.OfType<Puppy>().Any())
    {
        if (animal is Puppy puppy && !(animal is Puppy))
        {
            Console.WriteLine(puppy);
        }
    }
    else
    {
        Console.WriteLine(" You have no puppies");
        break;
    }
}
 
It's not complicated to check to see if the list contains any of a particular animal type. You are already doing it:
C#:
Pets.OfType<Puppy>().Any()

It's the way you are using the check, as well as your copy and paste code which is making things complicated.
 
Unfortunately, no. Many improvements need to be made.
 
Ouch! Many improvements as well. Thought that might be the case. Trial and error. Rinse and repeat. Any where to start?
 
Here's how I would do things:
C#:
class Animal
{
}

class Cat : Animal
{
}

class Dog : Animal
{
}

class Puppy : Dog
{
}

class Bird : Animal
{
}


class Program
{
    static void ShowAnimals<T>(IEnumerable<Animal> pets, string pluralAnimalName)
    {
        Console.WriteLine($"{pluralAnimalName}:");

        int count = 0;
        foreach (T animalType in pets.OfType<T>())
        {
            Console.WriteLine(animalType);
            count++;
        }

        if (count == 0)
            Console.WriteLine($" You have no {pluralAnimalName}.");
    }

    static void Main()
    {
        var pets = new List<Animal>()
        {
            new Dog(),
            new Cat(),
            new Dog(),
            new Cat(),
            new Puppy(),
        };

        ShowAnimals<Dog>(pets, "Dogs");
        ShowAnimals<Cat>(pets, "Cats");
        ShowAnimals<Puppy>(pets, "Puppies");
        ShowAnimals<Bird>(pets, "Birds");
    }
}

Which produces the following output:
C#:
Dogs:
Dog
Dog
Puppy
Cats:
Cat
Cat
Puppies:
Puppy
Birds:
 You have no Birds.

Notice that I don't make a distinction between Dogs and Puppies. Due to the inheritance relationship a Puppy IS-A Dog. That means that if you want to show dogs, you should also show puppies.

Anyway, all the code that you were cutting and pasting got moved into ShowAnimals<T>(). The only thing that was varying between each of the copy and pasted code is type and the string. So the method takes the type T, as well as a string pluralAnimalName.

Next, I removed the hard coded dependency on some class variable named Pets. Instead the method takes an IEnumerable<Animal> so that the code can be reused.

Next, I moved out the check for the existence of the type out of the foreach loop. Recall that Any() will actually iterate over the enumerable, but you were already iterating over the enumerable. So it didn't make sense to keep on searching the list multiple times. So the new foreach loop would just iterate over the animals of a particular type, and keep count of how many were seen so far.

I also moved out the logic for showing that there were no animals of a particular type. If the count of animals of a particular type seen is zero, then the string displaying that no such animals were found is displayed.
 
Wow that was less code indeed!
I've gone through it a couple of times and have to read more about IEnumerable. That's not something we've been tought yet.

I wonder though, I read that IEnumerable is readonly. Later on I'll probable want to be able to add and remove the amount of animals. Can this be done in some way using your solution?
 
Look at line 41, the example uses a List<T> that items is added to, and can be removed from.
 
Back
Top Bottom