I have a abstract, custom collection type we'lll call AnimalCollection for storing a collection of Animal objects. Animals that need to be stored in an animal collection are so special that they get their own implementation of AnimalCollection for storing additional information specific to that type of collection, e.g. TurtleCollection, GiraffeCollection, etc.
Since we're all collections here, I think it makes sense that AnimalCollection should implement IEnumerable<Animal> so users can iterate over it. However, in the case of our more derived classes it makes more sense to have implemented IEnumerable<Turtle>, IEnumerable<Giraffe>, etc.
class Animal {}
class Turtle : Animal {}
abstract class AnimalCollection : IEnumerable<Animal>
{
}
class TurtleCollection : AnimalCollection, IEnumerable<Turtle>
{
}
That way, when someone does foreach (var turtle in turtleCollection) var is automatically of type Turtle. Without having implemented IEnumerable<Turtle>, the user would have had to type foreach (Turtle turtle in turtleCollection). Since I always type var everywhere I can, I'm sure I would eventually trip over the fact my turtle variable wasn't casted to the type I expected, which means the users of my API would probably stumble over this as well!
So while I'm aware that having a type implement IEnumerable<T> on two adjacent types in an inheritance tree can cause issues, I'm wondering whether there could be hidden gotchas in using multiple IEnumerable<T> with types in the same inheritance hierarchy?
EDIT: The most sensible solution here would be to have AnimalCollection<T> and then define TurtleCollection : AnimalCollection<Turtle> as has been pointed out by some of the existing comments/answers. However the reason my AnimalCollection is non-generic is that the type I'm calling AnimalCollection actually represents a node in a tree. In actuality, I have types like
class Node {}
class FooNode : Node {}
class BarNode : Node {}
class NodeCollection : Node {}
class FooNodeCollection : NodeCollection {}
class BarNodeCollection : NodeCollection {}
FooNodeCollection is constrained in its constructor to only take objects of type FooNode. I then have visitors for accessing the nodes of a tree
class NodeVisitor
{
abstract void VisitFoo(FooNode foo);
abstract void VisitBar(BarNode bar);
abstract void VisitCollection(NodeCollection collection);
}
The exact type of node that is contained in the NodeCollection doesn't really matter as far as visitors are concerned; all the visitor will do is simply iterate over its children and have them dispatch themselves to the visitor.
Potentially one solution then could be to have two types - NodeCollection which is used by the visitor, and NodeCollection<T> which derives from NodeCollection and is the one that actually implements IEnumerable<T>, however I'm also interested to know what types of issues could arise from utilizing multiple IEnumerable<T> in the way originally stated