Freigeben über


My experiments with continuation in C#

The blogs are buzzing with continuation support in C# 2.0 and what cool things can be done with it. https://www.intertwingly.net/blog/2005/04/13/Continuations-for-Curmudgeons explains the concept of continuation extremely well. I also found Don Box's posting https://pluralsight.com/blogs/dbox/archive/2005/04/27/7780.aspx very interesting.

Traditionally in languages like C/C++ functions works off the stack. Each function call creates a new frame on the stack in which current context is stored (return address, current stack pointer) and then any arguments to the function is pushed in. The function also creates all its local variables on the stack and pops them off before the function returns. So when the function returns, all information regarding the functions execution is lost. So in case you call the function again it starts from the beginning.

However in continuation this is not what happens. Here the state of the function is preserved and when you call back on the function it starts off from where it left off. All the function locals are allocated on the heap so that their values are preserved when the function is called again. In C# we have a *very* restricted version of continuation using the yield return statement. Even though its restrictive it allows some very cool way of programming.

Here a sample of code where I have a container containing my friends names and I enumerate them.

public class MyContainer
{
string[] m_friends = { "Samrat", "Kaushik", "Rahul", "Abhinaba" };
public IEnumerator<string> GetEnumerator()
{
for (int index = 0; index < m_friends.Length; index++ )
yield return m_friends[index]; }
}

class Program
{
static void Main(string[] args)
{
        MyContainer myContainer = new MyContainer();
foreach (string str in myContainer)
Console.WriteLine(str);
}
}

You can have multiple enumerators too as in

public class CoolContainer
{
string[] m_friends = { "Samrat", "Kaushik", "Rahul", "Abhinaba" };
public IEnumerable<string> straight
{
get {
for (int index = 0; index < m_friends.Length; index++ )
yield return m_friends[index];
}
}
public IEnumerable<string> Reverse
{
get{
for (int index = m_friends.Length - 1; index >= 0; index--)
yield return m_friends[index];
}
}
}

class Program
{
static void Main(string[] args)
{
CoolContainer cc = new CoolContainer();

foreach (string str in cc.Reverse)
Console.WriteLine(str);

Console.WriteLine("----------------");

foreach (string str in cc.straight)
Console.WriteLine(str);
}
}

Seeing all these work I got carried away and tried to do the enumeration without the foreach as in

 

IEnumerator<string> enumerator = myContainer.GetEnumerator();

enumerator.MoveNext();

Console.WriteLine(enumerator.Current);

enumerator.MoveNext();

Console.WriteLine(enumerator.Current);

enumerator.Reset();

           

enumerator.MoveNext();

Console.WriteLine(enumerator.Current);

My idea was that after the call to Reset internally I would be able to reach the first element in the list as the index variable will be reset. That’s where I got the surprise in the form of a NotSupportedException in the call to Reset. I send emails to the CSharp user group and found out that its indeed not supported and you need to create new enumerator to go back or if the enumerator once runs till end (i.e. one return is executed) then you’ll automatically get the reset you wanted. This means that iterating twice or more using the same iterator works as in

MyContainer myContainer = new MyContainer();
foreach (string str in myContainer)
Console.WriteLine(str);
foreach (string str in myContainer)

    Console.WriteLine(str);

Comments

  • Anonymous
    August 13, 2005
    The comment has been removed
  • Anonymous
    January 13, 2006
    "...All the function locals are allocated on the heap so that their values are preserved when the function is called again..."
    Is this true? I thought that function locals should be on the stack, and continuation assures that these are not popped off. Please advise.
  • Anonymous
    January 13, 2006
    Its very hard to get continuations to work off the stack. In languages that implement continuation, function frames are allocated on the heap and not on the stack...