I was looking for an anwer to question Get next N elements from enumerable didn't find any satisfying and brewed my own. What I came up with was
IEnumerable<T> Chunk<T, R>(IEnumerable<R> src, int n, Func<IEnumerable<R>, T> action){
IEnumerable<R> head;
IEnumerable<R> tail = src;
while (tail.Any())
{
head = tail.Take(n);
tail = tail.Skip(n);
yield return action(head);
}
}
What I would really like though, is to have action have a default of t=>t
, but I can't figure out how to make that a default argument. The signature IEnumerable<T> Chunk<T, R>(IEnumerable<R> src, int n, Func<IEnumerable<R>, T> action = t=>t)
gives a syntax error.
My question is, how do I do this?
I suppose this is identical to Specifying a lambda function as default argument but for C# instead of C++
As a side note, I know it doesn't make any syntactical difference, but would it be easier to read if I switched T
and R
?
Default values have to be constants, and the only constant value for a delegate is a null
reference.
I suggest you use overloading instead. Note that t => t
wouldn't be valid anyway, unless you know that there's a conversion from IEnumerable<R>
to T
, which I don't see anywhere.
Aside from the lambda expression validity problem, you could use the null coalescing operator:
IEnumerable<T> Chunk<T, R>(IEnumerable<R> src, int n,
Func<IEnumerable<R>, T> action = null)
{
action = action ?? (t => t);
...
}
... but that would be sort of abusing it, and you wouldn't be able to tell whether the null
was actually from a caller who thought they were passing a non-null value, and would prefer you to raise an ArgumentNullException
.
EDIT: Additionally, note that your method is fundamentally problematic - iterating over the chunks will evaluate the original sequence several times (once per chunk) in order to skip the right amount. It would probably be better to write a method which eagerly read each chunk before returning it. We have a similar method in MoreLINQ, called Batch
. Note that Batch
has exactly the overload mentioned here - and in this case, the t => t
works, because the overload has fewer type parameters, so we know the identity conversion is okay.
It's the same for C#: create an overload.
IEnumerable<T> Chunk<T, R>(IEnumerable<R> src, int n){
return Chunk<T, R>(src, n, t => t);
}
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