March 2nd, 2010

FAQ :: Parallel.ForEach and non-generic collections?

.NET 2.0 introduced Generics to allow enhanced code reusability and type safety.  Since then, generic collections (IEnumerable<T>, List<T>, Dictionary<T>, etc.) have become standard and are recommended over their non-generic counterparts (IEnumerable, ArrayList, HashTable, etc.).  As a result, Parallel.ForEach only supports generic collections, so code like the following will fail to compile.

 

IEnumerable nonGenericCollection = …;

 

Parallel.ForEach(nonGenericCollection, currentElement =>

{

});

 

This issue applies to all non-generic collections (pretty much anything that was added before .NET 2.0), but here are some usual suspects that we’ve seen folks run into: XmlNodeList, DataRowCollection, DataTableCollection.  The error message is typically something like the following:

·         “The type arguments for method ‘System.Threading.Tasks.Parallel.ForEach<TSource>) cannot be inferred from the usage. Try specifying the type arguments explicitly.”

·         “The best overloaded method match for ‘System.Threading.Tasks.Parallel.ForEach<object>(System.Collections.Generic.IEnumerable<object>, System.Action<object>)’ has some invalid arguments.”

 

Fortunately, the workaround is simple.  Since non-generic collections produce objects (IEnumerator.Current returns Object), it is always possible to produce an IEnumerable<Object> from an IEnumerable.  For example:

 

static IEnumerable<object> Cast(IEnumerable source)

{

    foreach (object o in source)

        yield return o;

}

 

Even more fortunately, LINQ already provides this functionality:

 

public static IEnumerable<TResult>

    Cast<TResult>(this IEnumerable source);

 

So we can easily fix the initial example using this Cast extension method.

 

using System.Linq;

Parallel.ForEach(nonGenericCollection.Cast<object>(),

    currentElement =>

{

});

Author

0 comments

Discussion are closed.