Stacking the Predicate/Converter functions on top of the generic ForEach method...

I figured it would take me a few hours to pump out a decent ForEach sample, but I'm not thinking so.  At first I figured ForEach would provide a bunch of enumerator protection semantics, but it doesn't.  It just does a for loop over the internal array (and not because of the C# foreach optimization either, since this guy only has a single local int and they aren't making a copied reference of the _items array). 

Well hell, I can do that.  I've added the ForEach that now takes a list to mutate, a sub list predicate, a sub list converter (as in the last article), but adds the concept of a BreakAction.  Why a BreakAction?  Well, because the System.Action<T> delegate doesn't support a boolean return value.  How do you break out of a foreach statement?  Well, you use a break; call.  You can't do that with the System.Action<T> delgate, so I created a BreakAction<T> delegate that will work.  Note that BreakAction<T> now also uses SubType whereas the ForEach on the List<T> would make use of ListType.

Since delegates make heavy use of type parameters when declaring EVERYTHING, it wouldn't save you much code to put these helper methods on the List<T> now that I think about it.  ForEach by default could do what it does now, but as soon as you start adding the sub-list parsing, the calling code would look just as ugly as the runnable code sample here:

using System;
using System.Collections;
using System.Collections.Generic;

public class Organism {}
public class Plant : Organism {}
public class Animal : Organism {}
public class Carnivore : Animal {}
public class Herbivore : Animal {}
public interface ISeaPlant {}
public interface ILandPlant {}
public class SeaPlant : Plant, ISeaPlant {}
public class LandPlant : Plant, ILandPlant {}

public delegate bool BreakAction<DataType>(DataType dataToProcess);
public class GenericForEachEnhancement {

    private static void Main(string[] args) {
        List<Organism> organisms = new List<Organism>(30);
        for(int i = 0; i < 10; i++) {
            organisms.Add(new Plant());
            organisms.Add(new SeaPlant());
            organisms.Add(new LandPlant());
            organisms.Add(new Carnivore());
            organisms.Add(new Herbivore());
        }
       
        bool enumerationBreak = ForEach<Plant, Organism>(
            organisms,
            new Predicate<Organism>(PlantsOnlyPredicate),
            new Converter<Organism, Plant>(PlantsConverter),
            new BreakAction<Plant>(MyBreakAction));
           
        Console.WriteLine();
        Console.WriteLine("Enumeration Break?: {0}", enumerationBreak);
    }
   
    private static bool PlantsOnlyPredicate(Organism underlying) {
        if ( underlying is Plant ) { return true; } else { return false; }
    }

    private static Plant PlantsConverter(Organism input) {
        return input as Plant;
    }
   
    private static Random rand = new Random();
    private static bool MyBreakAction(Plant plantToProcess) {
        Console.WriteLine("Processing Plant");
       
        return (rand.Next(0, 30) == 0);
    }
   
    private static bool ForEach<SubType, ListType>(
        List<ListType> listToMutate,
        Predicate<ListType> subListPredicate,
        Converter<ListType, SubType> subListConverter,
        BreakAction<SubType> actionOrBreak) {
       
        // I would be using FindAllMutate probably, but I'll just rewrite the code here
        foreach(ListType listItem in listToMutate) {
            if ( subListPredicate(listItem) ) {
                if ( actionOrBreak(subListConverter(listItem)) ) {
                    return true;  // The enumeration was broken
                }
            }
        }
       
        // The enumeration full completed
        return false;
    }
}

The last step in the process (making this work for ControlCollection) will definitely have to come later.  Time for bed.

Published Friday, April 30, 2004 6:43 AM by Justin Rogers
Filed under:

Comments

Tuesday, January 22, 2008 1:31 AM by ranjeet

# re: Stacking the Predicate/Converter functions on top of the generic ForEach method...

href="/go/Airport/airportDetails.do?airportCode=BOI">BOI</a></td>

<td>Boise</td>

<td><a href="/go/Airport/airportDetails.do?airportCode=DEN">DEN</a></td>

<td>Denver</td>

<td>

9:43 AM

</td>

<td class="tableListingTableNoRightBorder">

9:34 AM

</td>

<td

<td>

11:30 AM

</td>

<td class="tableListingTableNoRightBorder">

11:30 AM

</td>

<td

<td><a href="/go/Airport/airportDetails.do?airportCode=DEN">DEN</a></td>

<td>Denver</td>

<td><a href="/go/Airport/airportDetails.do?airportCode=OMA">OMA</a></td>

<td>Omaha</td>

<td>

12:15 PM

</td>

<td class="tableListingTableNoRightBorder">

12:06 PM

</td>

<td class="tableListingTableNoLeftBorder">

</td>

<td>

2:36 PM

</td>

<td class="tableListingTableNoRightBorder">

2:50 PM

</td>