Wednesday, November 30, 2011

Unlocking the power of IEnumerable T

Since becoming proficient with using LINQ to find shit, I have never looked back. That is until I run into collection types that I can’t immediately query with LINQ expressions. And it seems like every time I encounter such a collection, my first course of action is hate on it for a little while. ControlCollection.FindControl hardly counts as a way to find controls and NameValueCollection has no apparent way to find things just to mention a couple. This goes for any of your custom collection types as well, unless you implemented your own search methods.

So one thing I have found myself doing time and time again is adding an extension method called AsEnumerable() to these types so that I don’t have to hate them. This method returns the collection as an IEnumerable<T>, unlocking a pile of extension methods that allow me to query and otherwise manipulate said collection in more ways than I can imagine.
Many collection classes in the framework already provide such a method, but many still do not. Also note that the non-generic IEnumerable is not the same as the generic one and is far lesser in power compared to its generic counterpart IEnumerable.
As an example, lets do this for the ControlCollection class so we can find all the controls on a page that have a certain css class.

public static IEnumerable<Control> AsEnumerable(this ControlCollection controls)
{
   foreach (Control ctrl in controls)
   {
       foreach (var subCtrl in ctrl.Controls.AsEnumerable())
       {
           yield return subCtrl;
       }
        yield return ctrl;
   }
}


Now that the extension is in place, we can write some code like this to find what we want:
protected void Page_Load(object sender, EventArgs e)
{
  IEnumerable<Control> blueControls = Controls.AsEnumerable().Where(c => c.CssClass.Contains(“blue”));
}

And for working with QueryStrings, this extension for the NameValueCollection class does the trick:
public static IEnumerable<KeyValuePair<string, string>> AsEnumerable(this NameValueCollection col)
{
    return col.Cast<string>().Select(key => new KeyValuePair<string, string>(key, col[key]));
}

Allowing us to do something like this:
Request.QueryString.AsEnumerable().Where(c => c.Key == "someParameter");

Go forth now and be cured of lame collection hate knowing that you can spend less time crawling collections and more time racing against the clock that is always counting, always ticking away at the relevancy of your current developer skill set.  Hell, with all the extra time you can spend now writing LINQ instead of wrestling with iterators, think of how much longer you can procrastinate now on that certification your boss has pestering you about...