views:

307

answers:

4

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...
}
+2  A: 

How about this?

IEnumerator<int> iter = obj.getInt();
using(iter) {
    while(iter.MoveNext()) {
        DoSomethingWith(iter.Current)
    }
}
zildjohn01
I don't think this will compile: MoveNext is on IEnumerator, not IEnumerable. You need to add a GetEnumerator() call.
itowlson
@itowlson, that was exactly what I was missing!
Mark
@itowlson -- You're right, my bad. Typo fixed.
zildjohn01
A: 

You can use this extension method

public static class GenericIEnumerableExtensions
{
    public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action)
    {
        foreach (var item in collection)
        {
            action(item);
        }
    }
}

Like this

IEnumerable<int> iter = getInt();
iter.ForEach(x => Console.WriteLine(x));
iter.ForEach(Console.WriteLine);          // A shortcut to the above syntax.
Mendy
+3  A: 

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);
}
CMS
Right. But what benefit are you get using this instead foreach?
Mendy
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.
@Anon: you can do the same with `foreach`.
Mendy
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.
@Anon: in the same way as you'd do it with the above syntax.
Mendy
In which case, while `foreach` might be syntactically alright, it's completely and utterly *wrong* semantically.
Anon.
@Anon: I don't see this. Can you give an example?
Mendy
`finally` blocks in a method that uses yield return are executed in the `Dispose` method of the `IEnumerator`. Not calling this method through `using` or whatever will cause unexpected behavior.try{yield return 1;}finally{ // this is executed by IEnumerator.Dispose}
Brandon Cuff
@Mendy: What do you consider to be the semantics of `foreach`?
Anon.
@Anon: iterate over IEnumerable. of course I can iterate only on half of it.
Mendy
@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".
Eric Lippert
+11  A: 

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.

Eric Lippert
@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
@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 Lippert
@Eric: I wonder about the colons here `new {Number: x, Amphibian: y}`. Is this JavaScript or C#? :)
Mendy
@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 D
@Mendy: yep, I haven't made that slip in a while. Apparently old habits die hard.
Eric Lippert
@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 Lippert
I like the approach. One step at a time. Like you do in Maths.
d.
@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.
Jason D
@Jason: If your point is that using sequence operators adds an extra call to MoveNext -- on the "where" iterator -- and an extra construction (of the tuple) then sure, I agree with you, that's added expense. It's pretty small expense, but it is extra. But if the sequences are huge, it is probably better to choose a better data structure than a pair of sequences so that the whole operation becomes cheaper.
Eric Lippert
@Eric, the point was there are three distinct scans of every element in the lists. There are only two lists. This is one more scan than absolutely necessary. It's really a .Net independent issue. One could do the same thing without LINQ or .Net and come up with similar results and performance concerns. (There would be more code of course) As for exploring a better data structure. I absolutely agree. However, sometimes incremental performance gains fit better with schedules. As always your mileage may vary, adapt to your own circumstance.
Jason D