Many custom Enumerable extensions can be implemented in terms of other builtin 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 force any PLINQ query back to sequential operation even though PLINQ also has a Any - and is equivalent with just just a signature change:
public static bool AnyOf<T>(this T item, ParallelQuery<T> items)
{
return items.Any(a => EqualityComparer<T>.Default.Equals(a, item));
}
But duplicating it like this seems messy to me.
At first I thought something like the below might work, but of course it does not^ because extension methods are static methods and therefore the decision to call Enumerable.Any
as opposed to ParallelQuery.Any
is made at compile time based on 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've come to the conclusion it's impossible without creating a copy of each method with a different signature, but maybe there's something I've missed. (Gee always with the impossible questions!)
Perhaps a better example of a helper that would benefit from parallelization (can obviously be chained, 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>'
Also worth considering is that not all of the ParallelQuery/Enumerable methods are equivalent, even if they do compile.
I have done similar for writing IQueryable/IEnumerable extensions. Trying to factor out the common bits involved declaring static variable holding an Expression, and then referencing that expression from the two different versions of the function. I don't have the code anymore, and when I was done, it was very ugly and I wasn't satisfied with it. 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 level of de-duplication was not good at all, and would be made more complicated by generic parameters. Maybe this will give you an idea though.
Looking forward to seeing others ideas.
You could do this by using checked casting inside the method (i.e. runtime switching) like so:
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));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With