Enhanced C#
Language of your choice: library documentation
Public Member Functions | Static Public Member Functions | List of all members
Loyc.Utilities.Co< T > Class Template Reference

A structure that helps you to write coroutines, or to avoid the performance penalty of nested iterators. More...

Source file:
Inheritance diagram for Loyc.Utilities.Co< T >:


A structure that helps you to write coroutines, or to avoid the performance penalty of nested iterators.

Extension methods for Co<T>.

This data type helps you solve the performance problem with using "yield return" recursively.

To illustrate the problem, let's consider the case of binary tree traversal. Suppose you define this data structure to hold a subtree of a sorted binary tree of strings:

internal class StringNode : IEnumerable<string>
internal StringNode LeftChild;
internal string Value;
internal StringNode RightChild;

You want to write an enumerator that returns all the strings in order. So you add this method to the StringNode class:

public IEnumerator<string> GetEnumerator()
foreach(string item in LeftChild)
yield return item;
yield return Value;
foreach(string item in RightChild)
yield return item;

As explained in this web page, this implementation will be slow, and it will get slower and slower as the tree gets deeper.

Co<T> (Co is short for "coroutine") helps to solve this problem with a postprocessing stage. Instead of writing GetEnumerator() directly as above, you write it like this instead:

public IEnumerator<string> GetEnumerator()
return GetCoEnumerator().Unwrap();
public IEnumerator<Co<string>> GetCoEnumerator()
yield return LeftChild.GetCoEnumerator().All();
yield return Value;
yield return RightChild.GetCoEnumerator().All();

Simple, right? String is automatically converted to Co{string} so that you can "yield return" one, but C# does not allow automatic coersion from IEnumerator{Co{T}} to Co{T} so you must add the suffix .All() which really means "wrap the IEnumerator{Co{T}} into a Co{T} object".

In fact, this class simulates the core functionality of C# 5's async/await feature just using C# 2's iterators. However, the C# 5 async feature is richer; for example, it supports exceptions properly. If an exception is thrown from within a nested iterator, the outer iterator is unable to catch the exception; it will be thrown directly into whatever foreach loop (or other code) that is enumerating the sequence. On the other hand, async is impoverished, since it can only produce a single output. Also, it is difficult to control how and when async methods are executed.

This class solves the same problem as Loyc.Collections.NestedEnumerator<F,T> in a way that is very easy to use. You should use Loyc.Collections.NestedEnumerator<F,T> instead if you need the highest possible performance.

Template Parameters
TType of item being enumerated.

Public Member Functions

 Co (IEnumerator< Co< T >> e)
 Co (T value)
IEnumerator< T > GetEnumerator ()
 Enumerates the underlying sequence. More...
System.Collections.IEnumerator System.Collections.IEnumerable. GetEnumerator ()

Static Public Member Functions

static implicit operator Co< T > (T e)
static Co< T > All< T > (this IEnumerator< Co< T >> sequence)
static IEnumerator< T > Unwrap< T > (this IEnumerator< Co< T >> sequence)

Member Function Documentation

◆ GetEnumerator()

IEnumerator<T> Loyc.Utilities.Co< T >.GetEnumerator ( )

Enumerates the underlying sequence.

IEnumerator< T > GetEnumerator()
Enumerates the underlying sequence.
Definition: Co.cs:85