收录日期:2020/12/03 00:34:07 时间:2010-09-07 20:49:58 标签:c#,.net,.net-4.0,extension-methods

I needed a double[] split into groups of x elements by stride y returning a List. Pretty basic...a loop and/or some linq and your all set. However, I have not been spending much time on extension methods and this looked like a good candidate for some practice. The naive version returns what I am looking for in my current application....

(A)
public static IList<T[]> Split<T>(this IEnumerable<T> source, int every, int take)
{
  /*... throw E if X is insane ...*/
  var result = source
               .Where ((t, i) => i % every == 0)
               .Select((t, i) => source.Skip(i * every).Take(take).ToArray())
               .ToList();
  return result;
}

...the return type is sort of generic...depending on your definition of generic.

I would think...

(B)    
public static IEnumerable<IEnumerable<T>> Split<T>
                  (this IEnumerable<T> source,int every, int take){/*...*/}

...is a better solution...maybe.

Question(s):

  • Is (B) preferred ?...Why ?
  • How would you cast (B) as IList <T[]> ?
  • Any benefit in refactoring ? possibly two methods that might be chained or the like.
  • Is the approach sound ?...or have I missed something basic.

Comments, opinions and harsh language are always appreciated.

Usage Context: C# .Net 4.0

B is probably the better option. Really the major change is that the consumer of the code has the option to make it a list using ToList() on the end of your method, instead of being forced to deal with a List (an IList, actually, which cannot be iterated).

This has a LOT of advantages in method chaining and general use. It's easy to ToList() an enumerable, but hard to go the other way. So, you can call Select().Split().OrderBy() on a list and use the results in a foreach statement without having to have Linq iterate through the whole thing at once.

Refactoring to yield return single values MIGHT get you a performance bonus, but since you're basically just returning the iterator that the Select gave you (which will yield one item at a time itself) I don't think you'll get much benefit in yielding through it yourself.

I would prefer (B) as it looks more flexible. One way of casting the output of the (B) method to an IList<T[]> is as simple as chaining .Select(x => x.ToArray()).ToList() to it, e.g.,

var foo = 
    bar.Split(someEvery, someTake).Select(x => x.ToArray()).ToList();

In .Net 4, you can just change the return type to IEnumerable<IEnumerable<T>> and it will work.

Before .Net 4, you would have to cast the internal lists to IEnumerable first, by just calling .Cast<IEnumerable<T>>() on your result before returning.