Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to clone an IEnumerable<T> instance, saving a copy of the iteration state?

I'd like to create a copy of an IEnumerator<T> so that I can restart the enumeration process from a particular location in the collection. Clearly, there is no benefit to doing so for collections that implement IList, since we can remember the index of interest.

Is there a clever way to accomplish this task using a combination of yield statements and Linq functions? I could not find a suitable Clone() method to copy the enumerator, and would like to avoid using Enumerable.Skip() to reposition a new enumerator to the desired resumption point.

Also, I'd like to keep the solutions as generic as possible, and not have to depend on state from any concrete collections.

like image 431
Steve Guidi Avatar asked Dec 16 '09 05:12

Steve Guidi


People also ask

What is IEnumerable T in C#?

IEnumerable. IEnumerable<T> contains a single method that you must implement when implementing this interface; GetEnumerator, which returns an IEnumerator<T> object. The returned IEnumerator<T> provides the ability to iterate through the collection by exposing a Current property.

How do you clone in C#?

C# | Clone() Method In C#, Clone() is a String method. It is used to clone the string object, which returns another copy of that data. In other words, it returns a reference to this instance of String. The return value will be only another view of the same data.

What is difference between IEnumerable and IEnumerator in C#?

IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. This works for readonly access to a collection that implements that IEnumerable can be used with a foreach statement. IEnumerator has two methods MoveNext and Reset. It also has a property called Current.

When we will use IEnumerable in C#?

IEnumerable is best to query data from in-memory collections like List, Array etc. IEnumerable doesn't support add or remove items from the list. Using IEnumerable we can find out the no of elements in the collection after iterating the collection. IEnumerable supports deferred execution.


1 Answers

The best you could do is write something that keeps a buffer (perhaps a Queue<T>) of the data consumed from one and not the other (which would get messy/expensive if you advanced one iterator by 1M positions, but left the other alone). I really think you would be better off rethinking the design, though, and just using GetEnumerator() (i.e. another foreach) to start again - or buffer the data (if short) in a list/array/whatever.

Nothing elegant built in.


Update: perhaps an interesting alternative design here is "PushLINQ"; rather than clone the iterator, it allows multiple "things" to consume the same data-feed at the same time.

In this example (lifted from Jon's page) we calculate multiple aggregates in parallel:

// Create the data source to watch
DataProducer<Voter> voters = new DataProducer<Voter>();

// Add the aggregators
IFuture<int> total = voters.Count();
IFuture<int> adults = voters.Count(voter => voter.Age >= 18);
IFuture<int> children = voters.Where(voter => voter.Age < 18).Count();
IFuture<int> youngest = voters.Min(voter => voter.Age);
IFuture<int> oldest = voters.Select(voter => voter.Age).Max();

// Push all the data through
voters.ProduceAndEnd(Voter.AllVoters());

// Write out the results
Console.WriteLine("Total voters: {0}", total.Value);
Console.WriteLine("Adult voters: {0}", adults.Value);
Console.WriteLine("Child voters: {0}", children.Value);
Console.WriteLine("Youngest vote age: {0}", youngest.Value);
Console.WriteLine("Oldest voter age: {0}", oldest.Value);
like image 140
Marc Gravell Avatar answered Sep 26 '22 02:09

Marc Gravell