Mark
Mark

Reputation: 108512

Using IEnumerable without foreach loop

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...
}

Upvotes: 41

Views: 62108

Answers (7)

Sith2021
Sith2021

Reputation: 3716

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;
}

Upvotes: 0

Christian C. Salvad&#243;
Christian C. Salvad&#243;

Reputation: 827218

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);
}

Upvotes: 73

vgru
vgru

Reputation: 51204

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();
    }
}

Upvotes: 2

sanjuro
sanjuro

Reputation: 1661

using for loop:

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

Upvotes: 2

Doug
Doug

Reputation: 35126

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;
}

Upvotes: 1

Eric Lippert
Eric Lippert

Reputation: 659964

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.

Upvotes: 34

zildjohn01
zildjohn01

Reputation: 11515

How about this?

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

Upvotes: 6

Related Questions