Omer van Kloeten's .NET Zen

Programming is life, the rest is mere details

News

Omer van Kloeten's Facebook profile

Omer has been professionally developing applications over the past 8 years, both at the IDF’s IT corps and later at the Sela Technology Center, but has had the programming bug ever since he can remember himself.
As a senior developer at NuConomy, a leading web analytics and advertising startup, he leads a wide range of technologies for its flagship products.

Get Firefox


powered by Dapper 

.NET Resources

Articles :: CodeDom

Articles :: nGineer

Culture

Projects

A Limitation of Lambda Expressions and Overloaded Extension Methods

Picture by Roger Ferrer Ibáñez, Some Rights Reserved (CC-NC-SA)Tamir hates lambdas. He was having a problem with one of his lambda expressions and twittered about it. Around that time I opened my twitter account (yes, Yosi finally convinced me) and offered my help.

He wanted to have a single extension method that could iterate over a collection and either change or keep the values it got. Kind of like this:

items.ForEach(item => item.SubItems.ForEach(subItem => subItem = newValue));

Where ForEach was defined as:

static void ForEach<T>(this IList<T> collection, Action<T> action)
{
    for (int i = 0; i < collection.Count; i++)
    {
        action(collection[i]);
    }
}

This did not work simply because subItem is a local variable - a copy of either the reference (when using a Reference Type) or of the value itself (when using a Value Type) - the lambda expression was (very loosely) translated to the following:

void Foo(SubItem subItem)
{
    subItem = newValue;
}

What I then tried to do was to replace Action<T> with a delegate with a ref parameter, so that the local variable will change. It turns out lambda expressions can not contain ref parameters.

On to the next solution - overloading the ForEach extension method:

static void ForEach<T>(this IList<T> collection, Action<T> action)
{
    for (int i = 0; i < collection.Count; i++)
    {
        action(collection[i]);
    }
}

static void ForEach<T>(this IList<T> collection, Func<T, T> action)
{
    for (int i = 0; i < collection.Count; i++)
    {
        collection[i] = action(collection[i]);
    }
}

This way, one can now write the following:

items.ForEach(item => item.SubItems.ForEach(subItem => newValue));

The first call (outer ForEach) will use the overload with Action<T> and the second (inner ForEach) will use the Func<T, T> overload and end up changing the current subItem. The C# compiler does a nice job finding the best overload to call.

Comments

Doron said:

I think that lambdas not being able to contain ref parameters is a good decision. They're supposed to be these simple, side-effect free functions, and anything with ref is neither of the above.

If you insist on using lambdas and extensions for this scenario, I think it might be better to use some kind of Map method that returns a new collection instead of editing the old one (and this will also work for collections that only implement IEnumerable and not IList).

# May 7, 2008 9:56 AM

Aaron Fischer said:

VB.net might work with the original syntax.  After all it will allow you to pass properties by ref(sorta).  Unless subitem was a field.

# May 7, 2008 9:59 AM

Omer van Kloeten said:

Doron - I see no reason to limit the power of lambdas in comparison to anonymous delegates and even methods.

I agree about the IEnumerable comment, but the original method required IList in order to be able to iterate by index.

Aaron - I have yet to dabble in VB.NET, sorry :)

# May 7, 2008 10:09 AM

Jason Haley said:

# May 7, 2008 4:25 PM

Bryan said:

Why do you guys use

 for (int i = 0; i < collection.Count; i++)

instead of

 foreach(<T> item in collection)

??

# August 19, 2008 4:38 PM

Omer van Kloeten said:

I assumed that since the collection implemented IList, it was faster to randomly access it via index than to access it sequentially using an enumerator.

# August 20, 2008 1:29 AM

Kirill Osenkov said:

In my recent post about coding styles one particular thing provoked the majority of feedback and discussions:

# February 1, 2009 2:42 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)