C-Sharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript | SQL | PHP | Angular | HTML
Info: We reference the System.Collections namespace for the first version, and the System.Collections.Generic namespace for the second.
Part A: In the part of the foreach-loop following the "in" keyword, there is a method call to ComputePower.
ForeachPart B: ComputePower() receives 2 parameters. It returns an IEnumerable<int> type, which we can use in a foreach-loop.
C# program that uses foreach, yield return
using System;
using System.Collections.Generic;
public class Program
{
static void Main()
{
// Part A: compute powers of two with the exponent of 30.
foreach (int value in ComputePower(2, 30))
{
Console.Write(value);
Console.Write(" ");
}
Console.WriteLine();
}
public static IEnumerable<int> ComputePower(int number, int exponent)
{
// Part B: continue loop until the exponent count is reached.
// ... Yield the current result.
int exponentNum = 0;
int numberResult = 1;
while (exponentNum < exponent)
{
// Multiply the result.
numberResult *= number;
exponentNum++;
// Return the result with yield.
yield return numberResult;
}
}
}
Tip: The compiler-generated class, marked with CompilerGenerated, instead uses several integral types.
Result: We see an entire class that is similar to how your code would look if you manually implemented the interfaces.
Info: The punctuation characters allow the compiler to ensure no naming conflicts will occur with your code.
Compiler-generated class: C#
// Nested Types
[CompilerGenerated]
private sealed class <ComputePower>d__0 : IEnumerable<int>,
IEnumerable, IEnumerator<int> // ...
{
// Fields
private int <>1__state;
private int <>2__current;
public int <>3__exponent;
public int <>3__number;
private int <>l__initialThreadId;
public int <exponentNum>5__1;
public int <numberResult>5__2;
public int exponent;
public int number;
// Methods [omitted]
}
Version 1: This version of the code uses a foreach-loop over an array and multiplies each element.
Version 2: Here we use a yield return method (one that returns IEnumerable) to perform the multiplication.
Result: The version that uses yield return runs many times slower. For simple things, avoid yield return for top speed.
C# program that benchmarks yield return
using System;
using System.Collections.Generic;
using System.Diagnostics;
class Program
{
const int _max = 1000000;
static void Main()
{
int[] numbers = { 10, 20, 30, 40, 50 };
var s1 = Stopwatch.StartNew();
// Version 1: use foreach with array.
for (int i = 0; i < _max; i++)
{
if (Method1(numbers) != 300)
{
throw new Exception();
}
}
s1.Stop();
var s2 = Stopwatch.StartNew();
// Version 2: use foreach with yield keyword.
for (int i = 0; i < _max; i++)
{
if (Method2(numbers) != 300)
{
throw new Exception();
}
}
s2.Stop();
Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) /
_max).ToString("0.00 ns"));
Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) /
_max).ToString("0.00 ns"));
}
static int Method1(int[] numbers)
{
// Sum with simple statements.
int sum = 0;
foreach (int number in numbers)
{
sum += number * 2;
}
return sum;
}
static int Method2(int[] numbers)
{
// Use yield return to get multiplied numbers and then sum those.
int sum = 0;
foreach (int number in GetNumbers(numbers))
{
sum += number;
}
return sum;
}
static IEnumerable<int> GetNumbers(int[] numbers)
{
// Return all numbers multiplied.
foreach (int number in numbers)
{
yield return number * 2;
}
}
}
Output
3.84 ns inline expression
50.68 ns yield return
Tip: This behavior does not exist in the Common Language Runtime. It is implemented by a class generated by the C# compiler.
Then: This is executed and JIT-compiled by the CLR. Yield is a form of syntactic sugar.
Note: It likely results in the integers being boxed, because IEnumerable does not have knowledge of their type and instead acts on Object.
Yield: Microsoft DocsTip: We can just loop over items from the infinite set until we encounter one that matches our requirements.
And: This reduces memory, reduces loading time of the set, and can also result in cleaner C# code.