41

I've gotta be missing something simple here.

Take the following code:

public IEnumerable<int> getInt(){
  for(int i = 0; i < 10; i++){
   yield return i;
  }
}

I can call this with:

foreach (int j in obj.getInt()){
  //do something with j
}

How can I use the getInt method without the foreach loop:

IEnumerable<int> iter = obj.getInt();
// do something with iter ??

Thanks.

EDITS

For those wondering why I'd want this. I'm iterating two things:

IEnumerator<int> iter = obj.getInt().GetEnumerator();
foreach(object x in xs){
  if (x.someCondition) continue;
  iter.MoveNext();
  int n = iter.current();
  x.someProp = n;
  etc...
}
3
  • What exactly are you trying to do? The whole point in IEnumerable<T> is to allow iteration, and a foreach loop is the most efficient way of doing this. Commented Feb 12, 2010 at 2:11
  • 1
    @Zakalwe, I want to iterate two enumerables at once. The foreach is moving through the first one, if it meets a condition, I need the next int (just an example, not really an int). Commented Feb 12, 2010 at 2:38
  • That´s very good question. In python it is very simple and I was investigating how to do it with C #, GetEnumerator is the answer Commented Oct 1, 2020 at 22:55

7 Answers 7

74

You can get a reference to the Enumerator, using the GetEnumerator method, then you can use the MoveNext() method to move on, and use the Current property to access your elements:

var enumerator = getInt().GetEnumerator();
while(enumerator.MoveNext())
{
    int n = enumerator.Current;
    Console.WriteLine(n);
}
Sign up to request clarification or add additional context in comments.

13 Comments

It allows you to do special processing on, say, the first few elements, or to stop entirely after processing however many, etc. Basically it gives you a little more versatility.
@Anon: you can do the same with foreach.
Elaborate on how you'd process some elements of the enumeration, go off and do something else entirely, and then come back and finish processing the rest with a foreach.
@Anon: like this: int i = 0; foreach(var x in y) { Process(x); i++; if (i == 5) GoOffAndDoSomethingDifferent(); }. See what happens there? You process the first few, then you go off and do something else entirely, and then you come back and finish processing the rest. "Go off and do something else entirely, pick up where you left off when it is finished" is a description of an operation we usually think of as "call a method".
@FitzchakYitzchaki One thing I use this for every now and then is to be able to enumerate over more than one enumerable at the same time. I don't think you can do that with foreach, and to do it with a for loop, you'd need the enumerables to at least be ICollections not IEnumerables.
|
34

My advice: don't mess around with the enumerators at all. Characterize your problem as a series of operations on sequences. Write code to express those operations. Let the sequence operators take care of managing the enumerators.

So let's see if I've got this straight. You have two sequences. Let's say { 2, 3, 5, 7, 12 } and { "frog", "toad" }. The logical operation you want to perform is, say "go through the first sequence. Every time you find a number divisible by three, get the next item in the second sequence. Do something with the resulting (number, amphibian) pair."

Easily done. First, filter the first sequence:

var filtered = firstSequence.Where(x=>x%3 == 0);

Next, zip the filtered sequence with the second sequence:

var zipped = filtered.Zip(
             secondSequence, 
             (y, z)=> new {Number = x, Amphibian = y});

And now you can iterate over the zipped sequence and do whatever you want with the pairs:

foreach(var pair in zipped)
    Console.WriteLine("{0} : {1}", pair.Number, pair.Amphibian);

Easy peasy, no messing about with enumerators.

9 Comments

@Eric, thanks, I like that, this is extremely close to what I need. I spend more time programming in python and it is very similar to how I'd do it there (I need to study up on the capabilities of C#). I'm curious though, what are the potential pitfalls of enumerators?
@Mark: (1) people forget to dispose them. (2) enumerators emphasize how the code works rather than what it is for -- reading code with enumerators is like looking at a description of a drill motor when what you want to describe is the resulting hole. They are certainly sometimes necessary, but I would much rather express my operations at a higher level of abstraction than one where I have to explicitly keep track of multiple "cursors" in multiple sequences.
@Eric, one caveat to your approach is that if this is acting on a large volume of in memory data, the may be a performance gain had by manually iterating the two lists concurrently... If it's a small amount of data or performance isn't a concern then I choose the elegant code (i.e. yours) over the nitty gritty code.
@Jason: all that Zip does is allocate two enumerators and run down them. The overhead should be tiny. Remember, the sequence operators are lazy; they don't realize their results all at once, they just pull from their source sequences on an as-requested basis.
@Eric, Tell me if I've missed anything here. Step 1. Iterate all of Collection 1 choosing only items of interest. Step 2. Iterate all of Collection 2, "joining it" with Collection 1. Step 3. Do something to each element in the pairing. Assume that each resulting collection is size of N, Big O gives us O(N) for the order of operations. GREAT!. Wonderful!. However, it is still 3*N for absolute number of operations. This is one more pass than absolutely necessary. The code is far simpler, therefore preferred for situations where the extra pass will not cause a perceivable performance hit.
|
6

How about this?

IEnumerator<int> iter = obj.getInt();
using(iter) {
    while(iter.MoveNext()) {
        DoSomethingWith(iter.Current)
    }
}

1 Comment

@itowlson, that was exactly what I was missing!
2

using for loop:

for (var enumerator = getInt().GetEnumerator(); enumerator.MoveNext(); )
{
    Console.WriteLine(enumerator.Current);
}

1 Comment

You must dispose the enumerator at the end
2

It's important to mention that the duty of the foreach loop is to dispose the enumerator if the instance implements IDisposable. In other words, foreach should be replaced with something like:

var enumerator = enumerable.GetEnumerator();

try
{
    while (enumerator.MoveNext())
    {
        var item = enumerator.Current;
        // do stuff
    }
}
finally
{
    var disposable = enumerator as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

Comments

1

Although the accepted answers are correct, notice that IEnumerator.Current is undefined before the first call to MoveNext().

If you are iterating over a secondary array, you'll want something like:

IEnumerable<Foo> Foo() { ... }

int AssignValues(List<TakesFoo> data) {
  var count = 0;
  var foo = Foo().GetEnumerator();

  // Step into the first element of the array;
  // doing this does not discard a value from the IEnumerator
  if (foo.MoveNext()) { 
    foreach (var x in data) {
      x.SetFoo(foo.Current);
      count += 1;
      if (!foo.MoveNext()) { 
        break;
      }
   }

   // Return count of assigned values
   return count;
}

Comments

0

Sample,

public static void SampleIteratorNext()
{
    var beauty = BeautifulLadies().GetEnumerator();

    Console.WriteLine($"Friday with {Next(beauty)} ...");
    Console.WriteLine($"Saturday with {Next(beauty)} ...");
    Console.WriteLine($"Tusday with {Next(beauty)} ...");
}

public static IEnumerable<string> BeautifulLadies()
{
    yield return "Scarlett";
    yield return "Alexandra";
    yield return "Alisson";
}

// emulate next() in python
public static T Next<T>(IEnumerator<T> iterator)
{
    iterator.MoveNext();
    return iterator.Current;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.