Brendan Enrick

Daily Software Development

Fun With Web Forms Controls and LINQ

Since LINQ has come out I’ve been very fascinated with it. LINQ to SQL is kind of cool, but working with in-memory collections is my favorite. Sure anything we can do with LINQ we could have done before, but now it’s easy. While not exactly the most practical and certainly not the most efficient method of handling things, working with a page’s Controls collection can be a lot of fun.

So perhaps I want to change the text of all of the TextBoxes on a page. I can easily grab that collection like this.

IEnumerable<TextBox> textBoxes = Page.Controls.
    Where(c => c.GetType() == typeof(TextBox)).Select(t => (TextBox)t);

Well sort of except that some controls will not be in that collection because this is a tree structure so I’ll make a recursive method which will get me all of the controls on the page.

public IEnumerable<Control> AllControls(Control root)
{
    if (root != null)
    {
        if (root.Controls.Count < 1)
        {
            yield return root;
        }
        foreach (Control c in root.Controls)
        {
            if (c.Controls.Count < 1)
            {
                yield return c;
            }
            else
            {
                foreach (var control in AllControls(c))
                {
                    yield return control;
                }
            }
        }
    }
}

Now we can work with a page having nested controls. Fun eh? Here is how you can get all of those controls and set the Text property of TextBox ones to their own IDs.

IEnumerable<TextBox> boxes = AllControls(Page).
    Where(c => c.GetType() == typeof(TextBox)).Select(t => ((TextBox)t));
foreach (var textBox in boxes)
{
    textBox.Text = textBox.ID;
}

Try it out with a page with textboxes in a login view. It works. Enjoy. Have fun!

LINQ Your Collections with IEqualityComparer and Lambda Expressions

Anyone using LINQ to manipulate in-memory collections is probably also using plenty of lambda expressions to make things quite easy. These two additions were really meant for each other. One of our interns here recently ran into an interesting problem while using LINQ. As a relatively new user of .NET based languages, reference types caused him a bit of trouble.

The problem

While using the dot notation with lambda expressions, he was using the Except method in the following way.

List<MyObject> x = myCollection.Except(otherCollection).ToList();

Well the problem here is that these two collections contain "MyObject"s, and when it does the comparison it does so based on the reference. This means if those are separate but equivalent objects that the comparison will claim they are different.

He had unit tests making sure that the except statement worked, but was using the same instance of variables to Assert, so the tests claimed to work.

I told him the problem and mentioned that there was probably an overload of Except that allows one to specify how to do the comparison. I was correct, but the overload takes an IEqualityComparer object. I was hoping for a Func<x,x,bool> as the second parameter, so I did what I always do; I Googled to see if anyone knew an easy way to get that to work without doing extra work.

The Internet was kind enough to inform me that there was no built in way of handling this situation.

Building your own was the suggestion. It is a pretty simple class, so it can just be tossed somewhere to be reused easily. It could easily come up and be needed again.

public class LambdaComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> _lambdaComparer;
    private readonly Func<T, int> _lambdaHash;

    public LambdaComparer(Func<T, T, bool> lambdaComparer) :
        this(lambdaComparer, o => 0)
    {
    }
    
    public LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
    {
        if (lambdaComparer == null)
            throw new ArgumentNullException("lambdaComparer");
        if (lambdaHash == null)
            throw new ArgumentNullException("lambdaHash");

        _lambdaComparer = lambdaComparer;
        _lambdaHash = lambdaHash;
    }

    public bool Equals(T x, T y)
    {
        return _lambdaComparer(x, y);
    }

    public int GetHashCode(T obj)
    {
        return _lambdaHash(obj);
    }
}

Now that we have a nice, Generic, comparer which can take lambda expressions, we are all set to plug this in to the previous code.

List<MyObject> x = myCollection.Except(otherCollection, 
  new LambdaComparer<MyObject>((x, y) => x.Id == y.Id)).ToList();

// or

IEqualityComparer comparer = new LambdaComparer<MyObject>((x, y) => x.Id == y.Id);
List<MyObject> x = myCollection.Except(otherCollection, comparer).ToList();

I admit I am still kind of annoyed that there wasn't an overload which just took a Func<T, T, bool> or a Func<T, T, int>.

Either way, I hope this helps someone use LINQ a little more easily. I know of some alternate ways of solving this same problem. So if you think I should have solved this differently then blog it and link back here or just post a comment below.

Update 16 April 2009 - From a comment below

One commenter below posted a suggested extension method for use with this. He suggests using this nice extension method so you can hide away the fact that you're using the custom comparer class.

public static class Ext
{
    public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, 
        IEnumerable<TSource> second , Func<TSource, TSource, bool> comparer )
    {
        return first.Except(second, new LambdaComparer<TSource>(comparer));
    }
}

Thank you for the comment. I like the idea. It will very nicely hide away the fact that a silly comparer is needed.