C# - Foreach loop iterating only over certain objects

Viewed 1253

I'm studying programming right now and I had to create a snake game.

In the game, there are around 5 possible consumables for the snake and I decided to put every new Consumable in a Consumable array.

Now I wanted to iterate with foreach loops through the array and do something like:

foreach (Apple apple in consumables)
{
    renderer.DrawApple(apple);
}

However when there are different objects in the array like an Apple and a SegmentRemover (which is possible, because they inherit from the Consumable class), then the compiler iterates over the SegmentRemover as well and I get an invalid cast exception.

I thought since I'm declaring in the foreach loop that I only want to iterate over Apple objects, that it should work.

Are there any easy ways to get around this? Preferably without things like var or typeof, since I'm not allowed to use these yet.

3 Answers

What you actually want is only those items that are "castable" to the type of Apple. For that use OfType<Apple> (uses linq):

foreach (Apple apple in consumables.OfType<Apple>())
{
    renderer.DrawApple(apple);
}

A non linq solution which will do pretty much the same is to use the as operator:

foreach(var item in consumbles)
{
    var apple = item as Apple;
    if(apple != null) 
    {
        renderer.DrawApple(apple);
    }
}

However IMO if all these items are placed in the same list, and as they all inherit from Consumble already, a better design all together, using polymorphisem, is to define for each type of Consumable a draw method as @dasblinkenlight suggested. And have specific implementations for each type. In the case of SegmentRemover it might be an empty implementation and in the Apple it might call the renderer.

You could flip the logic around, letting each consumable draw itself in a renderer:

interface IConsumable {
    void draw(Renderer renderer);
}
class Apple : IConsumable {
    void draw(Renderer renderer) {
        renderer.DrawApple(this);
    }
}
class SegmentRemover : IConsumable {
    void draw(Renderer renderer) {
        renderer.DrawSegmentRemover(this);
    }
}

Now your loop can go like this:

foreach (IConsumable consumable in consumables) {
    consumable.Draw(renderer);
}

This way you invoke Draw polymorphically on each instance of IConsumable, so the code that gets executed is decided at runtime by the actual class implementing IConsumable interface.

Your consumables is collection, are you trying to get to the collection with an instance of the object that stores the collection? if yes: Try:

foreach (Apple apple in consumables.consumables)
{
    renderer.DrawApple(apple);
}
Related