Click here to Skip to main content
16,020,103 members
Articles / Programming Languages / C#

ForEach elements with a derived type in an IEnumerable collection

Rate me:
Please Sign up or sign in to vote.
4.87/5 (10 votes)
10 May 2012CPOL4 min read 32.6K   437   13   12
An extension method for the IEnumerable interface to perform an action on each element which belong to a derived type of T.

Introduction 

This post will provide an extension method for the IEnumerable<T> and IEnumerable interface to perform an action on each element which belong to a derived type of T. Using this method you can invoke actions on a part of a collection with just one line of code.

Using the code  

In order to access the extension method include the following using directive in your program:

C#
using Rankep.CollectionExtensions;

You will need a collection which implements the IEnumerable<T> interface (for example: List<T>) on which the method will act. Example:

C#
IEnumerable<T> Vehicles = new List();

Now the following code can be used (it looks like the ForEach method of the List<T> class, but it will use our extension method) to loop through the whole collection, and call the Print method (defined by the Vehicle class) on each element:

C#
Vehicles.ForEach(v => v.Print());

Looping through the elements which have a given (derived) type (Car, in the example), can be done in two ways:

C#
Vehicles.ForEach<Vehicle, Car>(c => c.Print());

or:

C#
Vehicles.ForEach((Car c) => c.Print());

Both ways use the same ForEach<T, T2> extension method, but in the second example the types can be determined without explicitly defining them, by specifying the type of the lambda parameter.

A third extension method is available for collections which implement the non-generic IEnumerable interface. It can be used in the same way as the latter method for the generic interface.

An example for such collection would be the Children property of a WPF Grid element. It has a type called UIElementCollection, which implements the IEnumerable interface.

To change the size of all children elements of a Grid (called grid) that is an Ellipse, we can use the third extension method in the following way: 

C#
grid.Children.ForEach((Ellipse c) => { c.Width = 30; c.Height = 30; });

Motivation 

Imagine the following data structure:

Class diagram with the following relations: (Vehicle <- Ship, (Car <- Truck))

And a Vehicles variable which is a collection of Vehicle elements. (It implements the IEnumerable<Vehicle> interface).

If you would like to loop through the elements and invoke the Print method on them, then you could use the foreach construct:

C#
foreach (Vehicle item in Vehicles)
{
    item.Print();
}

And what if you would like to perform an action only on those elements which share a given derived type (e.g. Car)?

We would like to be able to write something like this:

C#
Vehicles.ForEach((Car c) => c.Print());

Solution 

How to loop through only the elements of the given type?

The first idea would be (at least mine was) that in the foreach statement specify the type of the element as a derived type, and hope for it will automagically work. Well, it doesn’t, it is not how foreach works. Foreach tries to cast each element in the collection to the type we specified, which will result in an InvalidCastException if there is any item which doesn’t belong to that type.

Okay, in the foreach we should use the base type. What if we check in the loop body if the current element is of the desired type and only execute the action when it is? This is exactly what the extension method will do. Let’s see a sample for the previous Vehicle <- Car example.

C#
foreach (Vehicle item in collection)
{
    if (item is Car)
    {
        action((Car)item);
    }
}

After generalizing it, here is the code of the extension method:

C#
public static IEnumerable<T> ForEach<T, T2>(this IEnumerable<T> collection, Action<T2> action) where T2 : T
{
    //The action can not be null
    if (action == null)
        throw new ArgumentNullException("action");
    //Loop through the collection
    foreach (var item in collection)
    {
        //If the current element belongs to the type on which the action should be performed
        if (item is T2)
        {
            //Then perform the action after casting the item to the derived type
            action((T2)item);
        }
    }
    //Return the original collection
    return collection;
}

It’s an extension method to the IEnumerable<T> interface.  The method takes two generic type parameters (T and T2), from which T is the type of the basic collection elements (e.g. Vehicle), and T2 is a derived type of T (e.g. Car) that will be used to filter on which elements the foreach action will be performed. The method also takes two parameters, the first will be the reference to the collection on which the method is invoked. The other will be an Action<T2> delegate, which is the action that will be performed on the collection elements of type T2.

Calling the method

The extension method can be invoked in the following way, using the previous Car and Vehicle classes:

C#
Vehicles.ForEach<Vehicle, Car>(c => c.Print());

If the type of the two generic types can be determined by the call the then explicit listing of them (<Vehicle, Car>) can be omitted. One way to do this is to specify the type of the c lambda argument:

C#
Vehicles.ForEach((Car c) => c.Print());

In this case the compiler will be able to determine the types, because T is given by the type of the Vehicles collection, and we explicitly specified T2 in the left side of the lambda expression.

Extension method for the whole collection

If we would like to use this extension method to perform an action on the whole collection, then we would have to specify the type of T2, which in this case will be the same as T. To allow omitting this redundant information we can define a new extension method which has only one generic parameter and simple calls the other extension method:

C#
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T> action)
{
    //Call the other extension method using the one generic type for both generic parameters
    return collection.ForEach(action);
}

Extending the non-generic IEnumerable interface

Both of the shown methods extend the generic IEnumerable<T> interface, but not every collection is generic. To make it possible to use these methods on non-generic ones, one more extension method will be provided, which extends the IEnumerable interface.

The method will only call the first extension method and then return the original collection.

In order to call that method we have to turn the non-generic collection into a generic one. To do this we will use the Cast<T>() method and the fact that everything inherits from the object class. 

C#
public static IEnumerable ForEach<T>(this IEnumerable collection, Action<T> action)
{
    //Cast the collection to an IEnumerable<T> and call the already defined extension method
    collection.Cast<object>().ForEach<object, T>(action); 

    return collection;
}  

Conclusion  

Thank you for reading, I hope you enjoyed and feel free to comment.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Student
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionInteractive Extensions already have ForEach Pin
Athari10-Oct-13 0:28
Athari10-Oct-13 0:28 
GeneralMy vote of 5 Pin
DuffmanLight8-Jun-12 6:04
DuffmanLight8-Jun-12 6:04 
GeneralRe: My vote of 5 Pin
Akos Orban8-Jun-12 8:21
Akos Orban8-Jun-12 8:21 
AnswerAbout another solution Pin
Juan Pablo G.C.8-May-12 21:28
Juan Pablo G.C.8-May-12 21:28 
Another way to do all the code you made is:

this.LayoutRoot.Children.OfType<System.Windows.Shapes.Ellipse>().ToList().ForEach(el => el.Width = 100);


But do not worry, in some cases, like in windows forms, the datagrid has datacolumns, datarows, and data that has not anyway to create the list easy, so your code is a greate solution for that case.
Juan Pablo G.C.
Mareinsula Blog


GeneralRe: About another solution Pin
Akos Orban10-May-12 19:52
Akos Orban10-May-12 19:52 
GeneralRe: About another solution Pin
mldisibio15-May-12 4:36
mldisibio15-May-12 4:36 
GeneralReinventing the wheel Pin
ekolis8-May-12 3:29
ekolis8-May-12 3:29 
GeneralRe: Reinventing the wheel Pin
Akos Orban8-May-12 6:22
Akos Orban8-May-12 6:22 
QuestionVery interesting using inherited classes, but... Pin
Juan Pablo G.C.8-May-12 2:18
Juan Pablo G.C.8-May-12 2:18 
AnswerRe: Very interesting using inherited classes, but... Pin
Akos Orban8-May-12 3:02
Akos Orban8-May-12 3:02 
GeneralRe: Very interesting using inherited classes, but... Pin
Juan Pablo G.C.8-May-12 8:38
Juan Pablo G.C.8-May-12 8:38 
GeneralRe: Very interesting using inherited classes, but... Pin
Akos Orban10-May-12 19:41
Akos Orban10-May-12 19:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.