flow.
"Flow is a condition of deep, nearly meditative involvement." - Tom DeMarco

Only iterating over the objects you want in a foreach loop

Thursday, February 22, 2007 4:36 PM

I've often wanted to do the following in code...

List<Animal> myAnimals = new List<Animal>();
myAnimals.Add(new Dog("Spot"));
myAnimals.Add(new Cat("Fluffy"));
myAnimals.Add(new Dog("Rover"));
foreach (Dog myDog in myAnimals)
{
    PetTheDog(myDog);
}

Basically, I want to have an enumerable collection of base class instances that I can just loop through, act on the types I want, and then just disregard the rest.  Seems like a small request, right?  Seems logical.  Heck, it even seems like it should even work.  I mean, foreach is an abstraction anyway, right?  Why can't it just do this for me?  I even tried it once just to make sure it really doesn't work.

It doesn't.

Everything's fine until you hit "Fluffy".  Then the runtime tries to cast Fluffy to a Dog...but Fluffy is a Cat, Fluffy doesn't want to be a Dog.  So we get an InvalidCastException and the whole thing falls down right in front of us.  I've played around with this off and on for awhile now and I've never been able to come up with a way to do it.  This has always been a thorn in my side because I create lists made up of base classes all the time in which in a few key spots must have their elements handled differently depending on what type of object they are.  This would be a great use for this but instead I always end up with the following:

foreach (Dog myDog in myAnimals)
{
    if (myDog is Dog)
    {
        PetTheDog(myDog);
    }
}

Don't get me wrong, this works fine.  It's never failed me, it just always seemed like it was more verbose than it needed to be.  It wasn't perfect but I could live with it I guess...until today.  Today, I happened on this amazing post by James Curran over at Honest Illusion.  As soon as I saw this, I knew that my dream was possible...and even better, James had solved all of the hard problems for me!

Borrowing heavily from Jame's example, I was able to create a class called GetObjectsOfType which would create an enumerator which would decide what to return based on the instance I asked for.  I've included the entire code for download here so I'll just show the meat and potatoes method of GetEnumerator() and the constructor which sets our collection below:

 

  public GetObjectsOfType(IEnumerable<BASE> enumerable)

{

_enumerable = enumerable;

}

 
  public IEnumerator<DERIVED> GetEnumerator()
  {
        // Get the enumerator from our original collection
        IEnumerator<BASE> iterator = _enumerable.GetEnumerator();
 
        // Loop through all elements of the collection
        while (iterator.MoveNext())
        {
            // If the current element is of the type we desire then return
            // it, otherwise just skip it
            if (iterator.Current is DERIVED)
            {
                yield return ((DERIVED)iterator.Current);
            }
        }
  } 
 

Basically I'm just creating an enumerator from the enumerable collection which is passed into our collection.  Then, in standard iterator fashion, I'm just looping through the collection and allowing us to return an iterator to it only if it is of type DERIVED.  What are BASE and DERIVED?  Well those are my generic types that I've created this class with as you can see from the class signature below:

 
public class GetObjectsOfType<BASE, DERIVED>  :  IEnumerable<DERIVED>
   where DERIVED : BASE

{

. . .

}

Basically, I'm asking for two types when GetObjectsOfType is created: BASE and DERIVED.  BASE is the common type shared by all elements of the collection, which in our initial example is Animal.  DERIVED represents the type that we're actually interested in retrieving, which was Dog in our initial example.  All objects which are not of type Dog are simply ignored and not returned to our foreach loop.  Note that GetObjectsOfType itself implements IEnumerable<DERIVED> which allows us to use it implicitly in the foreach loop.  Also note that I've added the constraint that DERIVED must derive from BASE.  In actuality this isn't really necessary, I've just added it to try and enforce the expected relationship between BASE and DERIVED.  In practice this may actually place an unnecessary restriction on the situations where you can use this class and you would likely gain some flexibility by removing it.  As previously stated, I've really only included it here for elegance of design and illustrative purposes. 

So, now that we have our class, how does this work in practice?  Well, let's take a look...

    // Create a list of Animals and fill it with both Dogs and Cats
    List<Animal> animals = new List<Animal>();
    animals.Add(new Cat("Fluffy"));
    animals.Add(new Dog("Spot"));
    animals.Add(new Dog("Lucky"));
    animals.Add(new Cat("Frisky"));
    animals.Add(new Dog("Fido"));
 
    // Now loop through the list, but only retrieve the Dogs
    foreach (Dog dog in
        new GetObjectsOfType<Animal, Dog>(animals))
    {
        Console.WriteLine("Found a dog named " + dog.Name);
    }

Here I've created a list of Animals containing both Dogs and Cats, but I'm only interested in working with the Dogs.  Well, I can simply create an instance of our GetObjectsOfType class inline in the foreach loop, tell it that I want to retrieve all instances of type Dog from my collection of Animals, and then just hand it the collection I want it to work with.  That's it!  The only elements that make it into the loop are elements of type Dog.  No more messy if statements, no more casting inline, everything is taken care of immediately.  Let's try it again with another collection...

        // Create a list of Students and fill it with both MaleStudents
        // and FemaleStudents
        List<Student> students = new List<Student>();
        students.Add(new FemaleStudent("Mary"));
        students.Add(new MaleStudent("Jim"));
        students.Add(new FemaleStudent("Betty"));
        students.Add(new MaleStudent("Michael"));
        students.Add(new FemaleStudent("Susan"));
 
        // Now loop through the list, but only retrieve the FemaleStudents
        foreach (FemaleStudent femaleStudent in
            new GetObjectsOfType<Student, FemaleStudent>(students))
        {
            Console.WriteLine("Found a female student named " +
                femaleStudent.Name);
        }

In this case I'm doing the same thing, except with MaleStudents and FemaleStudents.  I simply tell my class what types of objects I'm looking for, what types are in the list, and what collection to work from.  Everything else is taken care of automagically!

OK, ok...so this works great for custom classes.  What about other types like structs or primitives?  Well, everything derives from object...right?  The example below shows you how can apply this to standard types which aren't from my contrived examples...

        // Create a list of objects and fill it with all kinds of different
        // types.  
        List<object> objects = new List<object>();
        objects.Add("This is a string");
        objects.Add(34);
        objects.Add(100.0F);
        objects.Add(56);
        objects.Add(79.0);
 
        // Now loop through the list, but only retrieve the integers
        foreach (int integer in
            new GetObjectsOfType<object, int>(objects))
        {
            Console.WriteLine("Found an integer with a value of " +
                integer.ToString(Thread.CurrentThread.CurrentUICulture));
        }

In this case I'm simply creating a whole array of objects and filling it with all sorts of different types, but when the time comes all I want to work with are the integers.  Does it work?  You bet!

That's enough of my little examples.  Grab the code and give it a shot yourself.  I hope your as excited as me about this, I'm already thinking of dozens of places in my current codebase that I can go back and plug this in.  I hope you have some places you can use it as well, and if you do leave a note in the comments and tell me about it!

Once again, I want to thank James Curran at Honest Illusion for the inspiration for this post.  Great work, James!

kick it on DotNetKicks.com

Feedback

 re: Only iterating over the objects you want in a foreach loop

Uuuhhh... sorry. Thats a mess.
2/23/2007 4:46 PM | jc

# re: Only iterating over the objects you want in a foreach loop

Oh well! Each to his own! 2/24/2007 12:31 PM | Jeremy Jarrell

# re: Only iterating over the objects you want in a foreach loop

Looks complicated to me. Why not straight forward like that:

List<Animal> animals = new List<Animal>();
animals.Add(new Cat("Fluffy"));
animals.Add(new Dog("Spot"));
animals.Add(new Dog("Lucky"));
animals.Add(new Cat("Frisky"));
animals.Add(new Dog("Fido"));
foreach (Dog dog in animals.FindAll(delegate(Animal a) { return a is Dog; }))
{
Console.WriteLine("Found a dog named " + dog.Name);
} 2/25/2007 3:33 PM | Joachim Roppert

# re: Only iterating over the objects you want in a foreach loop

Hey, that's a great idea!

It never occurred to me to just drop an anonymous delegate in there. Thanks for the input! 2/25/2007 3:46 PM | Jeremy

 re: Only iterating over the objects you want in a foreach loop

I believe using lambda's in C#3.0 we could actually further simplify to

foreach (Dog dog in animals.FindAll(a=>a is Dog; ))


2/25/2007 4:33 PM | David

# re: Only iterating over the objects you want in a foreach loop

I wrote something similar a while back:

http://honestillusion.com/blogs/blog_0/archive/2006/11/07/Generics-without-Collections-_2800_pt-2_2900_.aspx 2/25/2007 11:18 PM | James Curran

# re: Only iterating over the objects you want in a foreach loop

You could also write a generic method.

public static class Utils {
public static IEnumerable<TDerived> GetObjectsOfType<TBase, TDerived>(IEnumerable<TBase> list) where TDerived : TBase {
foreach (TBase b in list) {
if (b is TDerived) {
yield return (TDerived)b;
}
}
}
}

You use the code like this:

foreach (Dog dog in Utils.GetObjectsOfType<Animal, Dog>(animals)) {
Console.WriteLine("Found a dog named " + dog.Name);
} 2/26/2007 7:03 AM | Fons Sonnemans

# re: Only iterating over the objects you want in a foreach loop

These are such great suggestions, I really like the lambda suggestion for 3.0 as well as the enhanced generics example from Fons. Keep the great suggestions coming! 2/26/2007 7:55 AM | Jeremy

# re: Only iterating over the objects you want in a foreach loop

James thanks for the comment, as I mentioned I sincerely enjoyed your original post and the other post you referenced seems very interesting as well. Looks like i may have to add another blog to my RSS reader... 2/26/2007 7:56 AM | Jeremy

# re: Only iterating over the objects you want in a foreach loop

Ooops... Sorry... I read through the code, and just skimmed the article, so I missed the mention of my post. Sorry for being redundant.... 2/26/2007 10:48 AM | James Curran

# re: Only iterating over the objects you want in a foreach loop

It seems .NET 3.0 will make this post irrelevant, but even still, I think a sense a bug in the code :)

You say this old standby works

foreach (Dog myDog in myAnimals)

{

if (myDog is Dog)

{

PetTheDog(myDog);

}

}


yet it looks to me like the cast should still fail once it hits a Cat. Did you perhaps mean:

foreach(Animal myAnimal in myAnimals) {
Dog myDog = myAnimal as Dog;
if(myDog != null) {
PetTheDog(myDog);
}
}

?
6/22/2007 3:27 PM | Matt

# re: Only iterating over the objects you want in a foreach loop

Yup, you're absolutely correct. That's a typo and should be

foreach (Animal myPotentialDog in myAnimals)
{
}

Otherwise you would get an InvalidCastException before you even got to the if statement. 6/22/2007 4:26 PM | Jeremy

Post a comment





 

Please add 8 and 6 and type the answer here: