DaveDev
DaveDev

Reputation: 42175

Why does 'return' and 'yield return' have the same behaviour in this example?

I was reading this article about fluent-style syntax and I tried the example. I noticed that when I converted the YoungDogs() and HerdingDogs() method bodies to LINQ expressions the yield return was replaced with return, but the behaviour of the method stayed the same.

Why does changing the method to a LINQ expression change the behaviour of how the data is returned?

This is the original example as outlined in the article:

    public static IEnumerable<Dog> YoungDogs(this IEnumerable<Dog> dogs)
    {
        foreach (Dog d in dogs)
            if (d.Age < 10)
                yield return d;
    }

    public static IEnumerable<Dog> HerdingDogs(this IEnumerable<Dog> dogs)
    {
        foreach (Dog d in dogs)
            if ((d.Breed == Breed.BorderCollie) ||
                (d.Breed == Breed.Collie) ||
                (d.Breed == Breed.Sheltie))
                yield return d;
    }

This is the full program with the changed methods:

class Program
{
    static void Main(string[] args)
    {
        foreach (Dog d in AllMyDogs().YoungDogs().HerdingDogs())
        {
            Console.WriteLine(d.ToString());
            if (d.Breed == Breed.JackRussell)
                break;
        }

        Console.ReadLine();
    }

    private static IEnumerable<Dog> AllMyDogs()
    {
        yield return new Dog("Kirby", Breed.BorderCollie, 14);
        yield return new Dog("Jack", Breed.JackRussell, 15);
        yield return new Dog("Ruby", Breed.Mutt, 4);
        yield return new Dog("Lassie", Breed.Collie, 19);
        yield return new Dog("Shep", Breed.Collie, 2);
        yield return new Dog("Foofoo", Breed.Sheltie, 8);
        yield return new Dog("Pongo", Breed.Dalmatian, 4);
        yield return new Dog("Rooster", Breed.WestHighlandTerrier, 1);
    }
}

static class DogFilters
{
    public static IEnumerable<Dog> YoungDogs(this IEnumerable<Dog> dogs)
    {
        return dogs.Where(d => d.Age < 10);
    }

    public static IEnumerable<Dog> HerdingDogs(this IEnumerable<Dog> dogs)
    {
        return dogs.Where(d => (d.Breed == Breed.BorderCollie) ||
                               (d.Breed == Breed.Collie) ||
                               (d.Breed == Breed.Sheltie));
    }
}

public enum Breed
{
    BorderCollie,
    Collie,
    Sheltie,
    JackRussell,
    Mutt,
    Dalmatian,
    WestHighlandTerrier
}

public class Dog
{
    public string Name { get; set; }
    public Breed Breed { get; set; }
    public int Age { get; set; }

    public Dog(string name, Breed breed, int age)
    {
        Name = name;
        Breed = breed;
        Age = age;
    }

    public bool TryBark(out string barkSound)
    {
        bool success = false;
        barkSound = "";

        if (Age <= 10)
        {
            success = true;
            barkSound = "Woof";
        }

        return success;
    }

    public string Bark()
    {
        string barkSound;

        if (!TryBark(out barkSound))
            throw new Exception("This dog can't bark");
        return barkSound;
    }

    public override string ToString()
    {
        return string.Format("{0} <{1}>, age {2}", Name, Breed.ToString(), Age);
    }
}

Upvotes: 3

Views: 105

Answers (1)

Christos
Christos

Reputation: 53958

This dogs.Where(d => d.Age < 10); declares a sequence, which when YoungDogs will be called will be returned. The thing that will be returned is the declaration of the sequence, not the dogs that "pass your filter". You will get the actual objects from dogs collection, only when you request them.

Under the hood Where encapsulates the call to the yield return through which the items in dogs that passes the predicate get returned one after the other. That happens for inastance below:

// That declares the sequence, you want to get. 
// In dogsWithAgeLessThanTen you don't have actaully any dog.
var dogsWithAgeLessThanTen = dogs.Where(d => d.Age < 10);

// Now you request the above query to be executed, that's called lazy loading. 
// Here you get one after the other the dogs that have age less than 10.
// The Enumerator you get from the dogsWithAgeLessThanTen returns your data 
// through a yield return statement.
foreach(var dog in dogsWithAgeLessThanTen)
{

}

The above is the same, regarding the results taht will be returned with the below one:

public static IEnumerable<Dog> YoungDogs(this IEnumerable<Dog> dogs)
{
    foreach (Dog d in dogs)
        if (d.Age < 10)
            yield return d;
}

var dogsWithAgeLessThanTen = dogs.YoungDogs();

foreach(var dog in dogsWithAgeLessThanTen)
{

}

Upvotes: 3

Related Questions