LINQ deferred loading using C# yield-return

Yield keyword exists in C# language since version 2 along with Visual Studio 2005, however it is rarely used by developers. Recently I read Demystifying the C# Yield-Return Mechanism article about yield-return keyword usage. This article gave three most common usage scenarios, but author (James McCaffrey) didn't mention LINQ. I think LINQ is where yield-return becomes most useful since it provides mechanism for deferred loading. Here is a quick example.

Lets say we have bunch of unit tests to run. It takes some time to run a single test, lets say 1 sec. For simplicity lets assume that even tests pass and odd fail:

public class Test
{
    public int Id { get; set; }        
    public bool Assert()
    {
        System.Threading.Thread.Sleep(1000);
        Console.WriteLine(string.Format("Test {0} was processed.", Id));            
        return Id % 2 == 0;
    }        
}

 

Lets write couple LINQ like extension methods for Test collections. Process1 doesn't use yield return. Process2 uses yield return and PrintPass output "Test # is passed" message.

public static class Extensions
{        
    public static IEnumerable<Test> Process1(this IEnumerable<Test> tests)
    {
        var result = new List<Test>();
        foreach (var test in tests)
        {
            if (test.Assert())
            {
                result.Add(test);
            }
        }
        return result;
    }
 
    public static IEnumerable<Test> Process2(this IEnumerable<Test> tests)
    {
        foreach (var test in tests)
        {
            if (test.Assert())
            {
                yield return test;
            }
        }
    }
 
    public static void PrintPass(this IEnumerable<Test> tests)
    {
        foreach (var t in tests)
        {
            Console.WriteLine(string.Format("Test {0} is passed.", t.Id));                
        }
    } 
}

 

Lets fill up test collection and compare output for Process1 and Process2 method:

class Program
{
    static void Main()
    {
        var tests = new List<Test>(new []
                        {
                            new Test {Id = 1},                                                         
                            new Test {Id = 2},
                            new Test {Id = 3},
                            new Test {Id = 4},
                            new Test {Id = 5},
                        });
 
        Console.WriteLine("**** No yield return *******");
        tests.Process1().PrintPass();            
        Console.WriteLine("**** Using yield return *******");
        tests.Process2().PrintPass();  
        Console.ReadKey();
    }
}

 

Output:

**** No yield return *******
Test 1 was processed.
Test 2 was processed.
Test 3 was processed.
Test 4 was processed.
Test 5 was processed.
Test 2 is passed.
Test 4 is passed.

**** Using yield return *******
Test 1 was processed.
Test 2 was processed.
Test 2 is passed.
Test 3 was processed.
Test 4 was processed.
Test 4 is passed.
Test 5 was processed.

As you can see, in first case whole collection was built before PrintPass method output anything. In second case you can see PrintPass prints "Test 2 is passed." right after Assert method for this test have done its processing.


Posted on Saturday, March 31, 2012 by | Add Comment

New Comment

Your Name:
Email (for internal use only):
Comment:
 
Code above:

Categories

Valid HTML5