I have three classes:
Animal
Giraffe : Animal
Tiger : Animal
... and want to implement three dictionary classes:
public abstract class AnimalDictionary
{
internal Dictionary<int, Animal> _internalDictionary;
//TODO: Dictionary interface implementation, minus the "Add" method
}
public class GiraffeDictionary : AnimalDictionary, IDictionary<int, Giraffe>
{
//Collection Code Examples
public List<Giraffe> GetGiraffesWithHerdID(int herdID);
public void FleeFromTigers(TigerDictionary pride);
}
public class TigerDictionary : AnimalDictionary, IDictionary<int, Tiger>
{
//Collection Code Examples
public List<Tiger> GetActiveHunters();
public void HuntGiraffeHerd(GiraffeDictionary targetHerd);
}
I'm well aware that the base AnimalDictionary
cannot implement IDictionary
or ICollection
because of the problems being able to put a Tiger in a GiraffeDictionary
would cause. I won't pretend to understand properly covariance and contravariance, but I do get why you can't put tigers in the giraffe pen.
Instead, I've implemented AnimalDictionary
by having it inherit IEnumerable<KeyValuePair<int, Animal>>
, and just made it implement all the same methods as a dictionary, minus Add()
, since Add() will be implemented in the derived classes.
Unfortunately, implementing IDictionary
in the derived collection classes has given me this bit of ugliness in GiraffeDictionary and TigerDictionary:
ICollection<Giraffe> IDictionary<int, Giraffe>.Values => _internalDictionary.Values.Cast<Giraffe>().ToList();
This concerns me, since I expect to be iterating over these collections in tight loops. I want to be able to iterate over the Values collection without the performance cost of casting and creating a whole new list, but Values is an ICollection, not an IEnumerable.
How can I resolve this problem? Is there an alternative approach that I'm missing, or should I give up on having a base class entirely and just accept that I'll have duplicate methods in the derived collection classes?
I think the generic AnimalDictionary<T>
will be helpful here and we can write the common method in the generic class which can be re-used by the other derived types. Here is a sample I composed which should give some idea how to approach this:
public class AnimalDictionary<T> where T : Animal
{
}
We can define the common generic method in the AnimalDictionary<T>
type so that we don't have two duplicate the common behaviors in the other types of Animal
and inside the generic type we can have a generic dictionary field like:
IDictionary<int,T> animalDictionary;
Here are the related stubs:
public class Animal
{
}
public class Cat : Animal
{
}
and the calling code would be like:
public static void Main()
{
AnimalDictionary<Cat> catDictionary = new AnimalDictionary<Cat>();
Console.WriteLine("Hello World");
}
Now we can add behaviors like this:
public class AnimalDictionary<T> where T : Animal
{
private IDictionary<int,T> animalDictionary = new Dictionary<int,T>();
public void AddAnimal(T animal)
{
animalDictionary.Add(animal);
}
}
and here is the calling side code :
public static void Main()
{
AnimalDictionary<Cat> animalDictionary = new AnimalDictionary<Cat>();
animalDictionary.AddAnimal(new Cat());
Console.WriteLine("Hello World");
}