Using PLINQ with Custom Enumerable Extensions

Many custom Enumerable extensions can be implemented in terms of other built-in operations - for example, this trivial convenience method:

public static bool AnyOf<TElement>(this TElement item, IEnumerable<TElement> items) { return items.Any(a => EqualityComparer<TElement>.Default.Equals(a, item)); } 

Now this will make any PLINQ query return to a sequential operation, even if PLINQ also has Any - and is equivalent to simply changing the signature:

 public static bool AnyOf<T>(this T item, ParallelQuery<T> items) { return items.Any(a => EqualityComparer<T>.Default.Equals(a, item)); } 

But duplication of this type seems dirty to me.

At first, I thought that something like below might work, but of course it is not, because extension methods are static methods, so the decision to call Enumerable.Any , unlike ParallelQuery.Any is executed at the time of compilation based on the signature.

 public static bool AnyOf<TElement, TEnumerable>(this TElement item, TEnumerable items) where TEnumerable : class, IEnumerable<TElement> { return items.Any(a => EqualityComparer<TElement>.Default.Equals(a, item)); } 

I came to the conclusion that this is impossible without creating a copy of each method with a different signature, but maybe I missed something there. (Come on always with impossible questions!)


Perhaps the best example of an assistant that would benefit from parallelization (obviously, maybe a chain, etc.) is something like this.

 public static IEnumerable<string> ToStrings(this IEnumerable<object> ienum) { return ienum.Select(a=> a.ToString()); } 

^ Compiler error:

  The type 'ParallelQuery<TElement>' cannot be used as type parameter 'TEnumerable' in the generic type or method 'AnyOf<TElement,TEnumerable>(TElement, TEnumerable)'. There is no implicit reference conversion from 'ParallelQuery<TElement>' to 'IEnumerable<TElement>' 

It is also worth considering that not all ParallelQuery / Enumerable methods are equivalent, even if they are compiled.

+7
source share
2 answers

I did similar to write IQueryable / IEnumerable extensions. Attempts to decompose common bits by declaring a static variable containing an expression, and then referring to this expression from two different versions of the function. I have no more code, and when I finished, it was very ugly, and I was not happy with that. Here is a trivial example.

 Expression<Func<PersonHistory, bool>> IsCurrent = (p) => p.Ends > DateTime.Now && p.Starts <= DateTime.Now; //Then in each Extension method: var query = db.PersonHistories.Where(IsCurrent); 

Ultimately, the deduplication rate was not very good, and it would be complicated using general parameters. Perhaps this will give you an idea.

Looking forward to see other ideas.

+1
source

You can do this using test casting inside the method (e.g., switching the runtime) as follows:

 public static bool AnyOf<TElement>(this TElement item, IEnumerable<TElement> items) { var parallelItems = items as ParallelQuery<TElement> if(parallelItems != null) { return parallelItems.Any(a => EqualityComparer<TElement>.Default.Equals(a, item)) } //other runtime checks .... //else return default IEnumerable implementation return items.Any(a => EqualityComparer<TElement>.Default.Equals(a, item)); } 
+1
source

All Articles