2

I'm developing an application. For argument's sake, let's say the application represents zoo(s). The data for each zoo is stored in a List <Zoo> type variable, where Zoo is defined as:

public class Zoo {
    List<Animal> animals;
    ...
}

Animal is defined as:

public abstract class Animal {
    public virtual string Name {get; set;};

    ...
}

And various classes derive from Animal:

public class Dog {
    public int nLegs = 4;
    public string furColour= "White";
    public Dog() {
        Name = "Dog";
    }
    ...
}

public class Millipede {
    public int nLegs = int.maxValue;
    public Millipede() {
        Name = "Millipede";
    }
    ...
}

public class Goldfish {
    public string colour = "Red";
    public Goldfish() {
        Name = "Goldfish";
    }
    ...
}

Let's say we live in a crazy world where each Zoo can only have one type of Animal (but as many different instances of Animal as they want). One particular Zoo really likes Millipedes:

List<Animal>.Add(new Millipede());
List<Animal>.Add(new Millipede());
List<Animal>.Add(new Millipede());
List<Animal>.Add(new Millipede());

Another Zoo really likes Goldfish:

List<Animal>.Add(new Goldfish());
List<Animal>.Add(new Goldfish());
List<Animal>.Add(new Goldfish());
List<Animal>.Add(new Goldfish());

In theory, the Animal class can be anything, and I have no way of knowing what different implementations will look like, but the specific Zoo class will. Ideally, I'd like to work with List<Dog> or List<Millipede>, and not List<Animal>, so I can access specific properties/methods of the Animal subclass. I've come across Convert List<DerivedClass> to List<BaseClass>, which lists a few options and describes why casting List<Animal> to List<Dog> doesn't make much sense. In my particular application, this conversion could be happening for many lists with a large number of items each (1000s), multiple times per second per list, so it's not feasible for me to do a conversion each time.

For an example like the above, what is the best way to organise the class structure, so that this problem can be avoided altogether?

Sahil Jain
  • 192
  • 1
  • 3
  • 10

2 Answers2

2

This is probably what you need:

public class Zoo<T> where T : Animal
{
    public List<T> animals = new List<T>();
}

public abstract class Animal
{
    public virtual string Name {get; set;}
}

public class Dog : Animal
{
    public int nLegs = 4;
    public string furColour= "White";
    public Dog()
    {
        Name = "Dog";
    }
}

public class Millipede : Animal
{
    public int nLegs = int.MaxValue;
    public Millipede()
    {
        Name = "Millipede";
    }
}

public class Goldfish : Animal
{
    public string colour = "Red";
    public Goldfish()
    {
        Name = "Goldfish";
    }
}

Now you can do this:

var zoo = new Zoo<Dog>();
zoo.animals.Add(new Dog());
zoo.animals.Add(new Dog());
zoo.animals.Add(new Dog());
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
-1

I think your best bet would be to try and track down the common behavior of every type of object you are planning to work with. This might be pretty easy in the case of a collection of animals (based on your example), but almost impossible if the objects differ very much from one to another (cars, computer peripherals, construction materials and food).

If you manage to find similarities in behavior between them, then your best bet would be using a general interface which should implemented by every object you are planning to use.

Based on your example, you could add something like this:

public enum AnimalGroupEnum
{
    Amphibian,
    Bird,
    Fish,
    Invertebrate,
    Mammal,
    Reptile,
}

Along with an interface:

public interface IAnimal
{
    AnimalGroupEnum AnimalGroup { get; }
} 

Then your Zoo class becomes:

public class Zoo 
{
    List<IAnimal> Animals
}

And all animals defined (Dog, Millipede or Goldfish) should implement this interface to be able to add it to the Animals collection. But doing this you would only have access to the AnimalGroup property when you go over the collection. Of course, this interface could contain other things which you need from your objects.

Hopefully this sheds some light over your question.

  • 1
    What does `AnimalGroupEnum` have to do with anything that the OP asked? – Enigmativity Jun 17 '17 at 09:32
  • @Enigmativity It has something to do with the architecture he's talking about. It would have been pretty weird to add `NumberOfLegs` for a class called `GoldFish`. I wanted to give an example for something which can be made common for each type of object. Also, your example (since I can only comment here) makes collecting everything under the same collection a bit difficult. It's possible, as can be seen here: https://stackoverflow.com/questions/3215402/collection-of-generic-types – Silviu Antochi Jun 17 '17 at 09:57