问题
I am dealing with trying to chunk up items in a custom collection class that implements IEnumerable (and ICollection) in C# 2.0. Let's say, for example, that I only want 1000 items at a time and I have 3005 items in my collection. I've got a working solution that I demonstrate below, but it seems so primitive that I figure there has to be a better way to do this.
Here's what I have (for example's sake, I'm using C# 3.0's Enumerable and var, just replace those references with a custom class in your mind):
var items = Enumerable.Range(0, 3005).ToList();
int count = items.Count();
int currentCount = 0, limit = 0, iteration = 1;
List<int> temp = new List<int>();
while (currentCount < count)
{
limit = count - currentCount;
if (limit > 1000)
{
limit = 1000 * iteration;
}
else
{
limit += 1000 * (iteration - 1);
}
for (int i = currentCount; i < limit; i++)
{
temp.Add(items[i]);
}
//do something with temp
currentCount += temp.Count;
iteration++;
temp.Clear();
}
Can anyone suggest a more elegant way of doing this in C# 2.0? I know if this project was from the past 5 years I could use Linq (as demonstrated here and here). I know my method will work, but I'd rather not have my name associated with such ugly (in my opinion) code.
Thanks.
回答1:
Firstly . yield
is your friend here, and it was introduced with 2.0. Consider:
public static IEnumerable<List<T>> Chunk<T>(IEnumerable<T> source, int chunkSize)
{
List<T> list = new List<T>(chunkSize);
foreach(T item in source)
{
list.Add(item);
if(list.Count == chunkSize)
{
yield return list;
list = new List<T>(chunkSize);
}
}
//don't forget the last one!
if(list.Count != 0)
yield return list;
}
Then we're flexible in type and size, so it's nicely reusable. The only that being restricted to 2.0 means, is that we can't make it an extension method.
回答2:
There are several ways you could approach this.
If you just want to associate each item with the index of the chunk it belongs to:
int processed = 0;
foreach (int item in items)
{
int chunkIndex = processed++ / CHUNK_SIZE;
ProcessItem(item, chunkIndex);
}
If you want to process items in batches, but don't need the whole chunk collection at once:
int processed = 0, count = items.Count;
List<int> chunk = new List<int>(CHUNK_SIZE);
foreach (int item in items)
{
chunk.Add(item);
if (++processed % CHUNK_SIZE == 0 || processed == count) {
ProcessChunk(chunk);
chunk.Clear();
}
}
If you want to have all chunks as a list of lists:
int processed = 0, count = items.Count;
List<List<int>> chunks = new List<List<int>>();
foreach (int item in items)
{
int chunkIndex = processed++ / CHUNK_SIZE;
if (chunks.Count == chunkIndex) {
chunks.Add(new List<int>(CHUNK_SIZE));
}
chunks[chunkIndex].Add(item);
}
来源:https://stackoverflow.com/questions/12186376/chunk-ienumerable-icollection-class-c-sharp-2-0