Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ability to reset IEnumerator generated using yield (C#)

Tags:

If I use yield instead of manually creating an IEnumerator, is it possible to implement IEnumerator.Reset?

like image 268
Casebash Avatar asked Feb 22 '10 00:02

Casebash


People also ask

What does yield do in C?

The yield keyword tells the compiler that the method in which it appears is an iterator block. yield return <expression>; yield break; The yield return statement returns one element at a time. The return type of yield keyword is either IEnumerable or IEnumerator .

What is the benefit of yield C#?

The advantage of using yield is that if the function consuming your data simply needs the first item of the collection, the rest of the items won't be created so it's more efficient. The yield operator allows the creation of items as it is demanded. That's a good reason to use it.

When should I use yield in C#?

You use a yield return statement to return each element one at a time. The sequence returned from an iterator method can be consumed by using a foreach statement or LINQ query. Each iteration of the foreach loop calls the iterator method.

What is yield break in C#?

It specifies that an iterator has come to an end. You can think of yield break as a return statement which does not return a value. For example, if you define a function as an iterator, the body of the function may look like this: for (int i = 0; i < 5; i++) { yield return i; } Console.


2 Answers

No, it is not possible. When the C# compiler processes an iterator (a method that contains a yield statement), the compiler generates a class that implements IEnumerable and IEnumerator. The generated class' implementation of Reset just throws a NotSupportedException. There is no way to influence this in current versions of C#.

Instead, your calling code will need to request a new enumerator, i.e. begin a new foreach loop. Or you will need to forgo the language support (the yield statement) and write your own class which implements IEnumerator.

like image 77
itowlson Avatar answered Sep 16 '22 17:09

itowlson


There is no built-in support, but you can define your own implementation of IEnumerator that delegates all method calls to the enumerator generated by C# and only lets you define your own behavior for the Reset method.

The simplest version of the class would look like this:

class ResetableEnumerator<T> : IEnumerator<T>
{
  public IEnumerator<T> Enumerator { get; set; }
  public Func<IEnumerator<T>> ResetFunc { get; set; }

  public T Current { get { return Enumerator.Current; } }
  public void  Dispose() { Enumerator.Dispose(); }
  object IEnumerator.Current { get { return Current; } }
  public bool  MoveNext() { return Enumerator.MoveNext(); }
  public void  Reset() { Enumerator = ResetFunc(); }
}

In this case, the ResetFunc that you specify returns a new IEnumerator<T>, so your provided implementation of ResetFunc can do some cleanup or whatever you need to do when resetting and then return a new enumerator.

IEnumerator<int> Foo() { /* using yield return */ }
IEnumerator<int> PublicFoo() {
  return new ResetableEnumerator<int> { 
    Enumerator = Foo(),
    ResetFunc = () => { 
      Cleanup();
      return Foo(); } };
}

You'll need to store all the originally local variables of the Foo method as fields of the class, so that you can access them in Cleanup (Note that the rest of the Foo body will never be executed after calling Reset), but that's still easier than writing a handwritten iterator!

like image 43
Tomas Petricek Avatar answered Sep 17 '22 17:09

Tomas Petricek