11 out of 22 rated this helpful - Rate this topic

yield (C# Reference)

The yield keyword signals to the compiler that the method in which it appears is an iterator block. The compiler generates a class to implement the behavior that is expressed in the iterator block. In the iterator block, the yield keyword is used together with the return keyword to provide a value to the enumerator object. This is the value that is returned, for example, in each loop of a foreach statement. The yield keyword is also used with break to signal the end of iteration. For more information about iterators, see Iterators (C# Programming Guide). The following example shows the two forms of the yield statement.

yield return <expression>;
yield break;

In a yield return statement, expression is evaluated and returned as a value to the enumerator object; expression has to be implicitly convertible to the yield type of the iterator.

In a yield break statement, control is unconditionally returned to the caller of the iterator, which is either the IEnumerator.MoveNext method (or its generic System.Collections.Generic.IEnumerable<T> counterpart) or the Dispose method of the enumerator object.

The yield statement can only appear inside an iterator block, which can be implemented as the body of a method, operator, or accessor. The body of such methods, operators, or accessors is controlled by the following restrictions:

  • Unsafe blocks are not allowed.

  • Parameters to the method, operator, or accessor cannot be ref or out.

  • A yield return statement cannot be located anywhere inside a try-catch block. It can be located in a try block if the try block is followed by a finally block.

  • A yield break statement may be located in a try block or a catch block but not a finally block.

A yield statement cannot appear in an anonymous method. For more information, see Anonymous Methods (C# Programming Guide).

When used with expression, a yield return statement cannot appear in a catch block or in a try block that has one or more catch clauses. For more information, see Exception Handling Statements (C# Reference).

In the following example, the yield statement is used inside an iterator block, which is the method Power(int number, int power). When the Power method is invoked, it returns an enumerable object that contains the powers of a number. Notice that the return type of the Power method is System.Collections.IEnumerable, an iterator interface type.


public class PowersOf2
{
    public static System.Collections.IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }

    static void Main()
    {
        // Display powers of 2 up to the exponent 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }
}
/*
Output:
2 4 8 16 32 64 128 256 
*/


For more information, see the C# Language Specification. The language specification is the definitive source for C# syntax and usage.

Did you find this helpful?
(1500 characters remaining)
Community Content Add
Annotations FAQ
Automaticly implemented enumerators do not clean up managed resources
using System;
using System.Collections.Generic;
internal static class Test{
private static void Main(){
try{
Console.WriteLine("{0,10}: Start point",GC.GetTotalMemory(true));
IEnumerable<int> bigManagedResourceEnumerable=BigManagedResourceEnumerable();
Console.WriteLine("{0,10}: Big managed resource enumerable created",GC.GetTotalMemory(true));
IEnumerator<int> bigManagedResourceEnumerator1=bigManagedResourceEnumerable.GetEnumerator();
Console.WriteLine("{0,10}: First big managed resource enumerator created, and enumerator is{1} reference equals to enumerable",GC.GetTotalMemory(true),object.ReferenceEquals(bigManagedResourceEnumerator1,bigManagedResourceEnumerable)?"":" not");
bigManagedResourceEnumerator1.MoveNext();
Console.WriteLine("{0,10}: MoveNext called first time",GC.GetTotalMemory(true));
bigManagedResourceEnumerator1.MoveNext();
Console.WriteLine("{0,10}: MoveNext called second time, enumeration ended, but memory not freed",GC.GetTotalMemory(true));
bigManagedResourceEnumerator1.Dispose();
Console.WriteLine("{0,10}: Dispose called, but memory not freed",GC.GetTotalMemory(true));
GC.KeepAlive(bigManagedResourceEnumerator1);
bigManagedResourceEnumerator1=null;
Console.WriteLine("{0,10}: First big managed resource enumerator removed, but memory not freed",GC.GetTotalMemory(true));
IEnumerator<int> bigManagedResourceEnumerator2=bigManagedResourceEnumerable.GetEnumerator();
Console.WriteLine("{0,10}: Second big managed resource enumerator created, and enumerator is{1} reference equals to enumerable",GC.GetTotalMemory(true),object.ReferenceEquals(bigManagedResourceEnumerator2,bigManagedResourceEnumerable)?"":" not");
bigManagedResourceEnumerator2.MoveNext();
Console.WriteLine("{0,10}: MoveNext called first time",GC.GetTotalMemory(true));
bigManagedResourceEnumerator2.MoveNext();
Console.WriteLine("{0,10}: MoveNext called second time, enumeration ended, but memory not freed",GC.GetTotalMemory(true));
bigManagedResourceEnumerator2.Dispose();
Console.WriteLine("{0,10}: Dispose called, but memory not freed",GC.GetTotalMemory(true));
GC.KeepAlive(bigManagedResourceEnumerator2);
bigManagedResourceEnumerator2=null;
Console.WriteLine("{0,10}: Second big managed resource enumerator removed, memory of second big managed resource enumerator freed, at last",GC.GetTotalMemory(true));
GC.KeepAlive(bigManagedResourceEnumerable);
bigManagedResourceEnumerable=null;
Console.WriteLine("{0,10}: Big managed resource enumerable removed, memory of first big managed resource enumerator freed, at last",GC.GetTotalMemory(true));
}catch(Exception e){
Console.WriteLine(e);
}
Console.ReadKey(true);
}
private static IEnumerable<int> BigManagedResourceEnumerable(){
byte[] bigManagedResource=new byte[100000000];
yield return bigManagedResource.Length;
}
}


I didn't post the code here, but here is a comment about it.
As running the code demonstrates, the first IEnumerator is the same object as the BigManagedResourceEnumerable.
If the Enumerable reference were to go out of scope, the IEnumerator would be the only reference and removing it would clean up the resources. Hanging on to a reference to the Enumerable after the first IEnumerator is removed will keep mmemory around, because the Enumerable object will not be reused as an IEnumerator.

Dispose shouldn't be expected to clean anything up as these are managed objects.

I guess the moral of the story is: don't hang onto IEnumerables very long, if you don't know how they were genereated.


>Dispose shouldn't be expected to clean anything up as these are managed objects.
Dispose should be expected to release reference to bigManagedResource, so garbage collector can collect it, although it can not collect enumerator itself.