I suggest you ...

Include a 'yield throw' statement syntax.

The yield return/break syntax is incredibly useful for swift generation of enumerations, and is a fantastic piece of syntactic sugar, both speeding the programmer and providing a harder-to-break approach for making enumerations.

We have 'yield return' and 'yield break'. But there is no 'yield throw'. Only two out of three of the block-exiting commands.

At present, if an exception is thrown inside the body of a generator method, the MoveNext() method of the generated IEnumerator throws this exception, halting any iteration over the enumeration, with MoveNext() now returning false. It also does not advance the internal state machine any further than where the exception was thrown (in particular, if control was inside any kind of for loop with an update step, even if the exception is thrown as the last statement of the loop body.)

All of this is fine, and consistent with other parts of the language. It does not, though, provide great control if the thrown objects are intentionally part of the behaviour of the enumeration. I suggest that a valuable addition would be to include a 'yield throw' statement. The result would be that the MoveNext() method advance the state machine as far as, but no further than the 'yield throw' statement. Any subsequent calls to the Current property would subsequently carry out the throw (much as they would carry out a return for a 'yield return' statement). The next call to MoveNext() would proceed to move the state machine as far as the next yield statement and then behave appropriately. I believe this would be the intuitive semantic understanding of this syntax. It would not break any existing code, unless it relied heavily on calls to IEnumerator.Current never throwing an exception (which is not part of any specification).

The output of the PrintAllTest method in the attached file, for the IEnumerator in the existing syntax is:
1
2
.MoveNext() threw Exception

The desired and expected output for the PrintAllTest method for an IEnumerator in the suggested syntax would be:
1
2
.Current threw Exception
4
5

While IEnumerators can be written which have the behaviour described above, they preclude any use of the yield syntax, making for more unwieldy and time-consuming code. I hope this is a convincing explanation of the extra expressiveness this idea could bring to the language.

21 votes
Vote
Sign in
(thinking…)
Password icon
Signed in as (Sign out)
You have left! (?) (thinking…)
Oly Sourbut shared this idea  ·   ·  Flag idea as inappropriate…  ·  Admin →

1 comment

Sign in
(thinking…)
Password icon
Signed in as (Sign out)
Submitting...
  • Philippe commented  ·   ·  Flag as inappropriate

    Typically one should avoid calling MoveNext()/Current and used foreach instead. In that case, this would not works.

    However, it would be quite easy to do a generic class that has 2 properties : Item and Error.

    public class ItemOrError<T>
    {
    public ItemOrError(T item) { this.item = item; }
    public ItemOrError(Exception error) { Error = error; }

    public T Item
    {
    get { if (Error != null) throw Error; return item; }
    }

    public Exception Error { get; private set; }

    private T item;
    }

    To generate and error one would write yield return new ItemOrError<T>(new Exception());

    That solution is far better because it return an explicite proxy and the user will not be surprise that the iteration could throw... The use of ItemOrError class would clearly show that the collection can throw errors...

    It is very weird to have an enumeration that would throw on some items. Usually, you either want to skip bas items in which case the caller would never see the problem or thrown an exception and the caller stop enumerating items because of the exception.

    For the rare case where it would be useful to throw exception for some items, the a solution like mine would be appropriate.

    Also throwing a lot of exception from a loop can have a serious impact on performance... and thus again my approach would be better.

    I really don't think that the langage should be make more complicated for features like that that almost nobody would use and understand.

Feedback and Knowledge Base