Sunday 26 February 2017

IEnumerable with Yield

Previously we went through the trouble of creating a person class and a people class that implemented the IEnumerable interface, which forced us to define a GetEnumerator() function that forced us to create a PeopleEnumerator that implemented the IEnumertor interlace which had some methods from other interfaces that needed to be implemented, anyway it turned out to be a bit of a pain. Now that we know about the Yield Keyword that word of hurt just got a whole lot smaller.

Again let's look at our Person class

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public override string ToString() { return $"{FirstName} {LastName}"; }

}

Same as before, but now let's take a look at our People class

class People : IEnumerable<Person>
{
    Person[] _ppl;
    public People(params Person[] ppl) { _ppl = ppl; }

    public Person this[int Index]
    {
        get { return _ppl[Index]; }
        set { _ppl[Index] = value; }
    }

    public int Length { get { return _ppl == null ? 0 : _ppl.Length; } }

    public IEnumerator<Person> GetEnumerator() {
        for (int i = 0; i < _ppl.Length; i++)
            yield return _ppl[i];
    }
    IEnumerator IEnumerable.GetEnumerator() {
        return GetEnumerator();
    }

}

Notice that instead of having to have to go through the pain of manually creating an enumerator, we just use the yield keyworld instead. In the background when our code is compiled to the Microsoft Intermediate language the compiler builds out the enumerator for us using magic...

anyway here's a main to test our implementation of the yield keyword

class Program
{
    static void Main(string[] args)
    {
        var p1 = new Person { FirstName = "Pawel", LastName = "Ciucias" };
        var p2 = new Person { FirstName = "Tomek", LastName = "Ciucias" };
        var p3 = new Person { FirstName = "Jakub", LastName = "Tywoniuk" };
        var p4 = new Person { FirstName = "Magda", LastName = "Tywoniuk" };

        //becasue of the params key word
        var ppl = new People(p1, p2, p3, p4);

        for (var i = 0; i < ppl.Length; i++)
            Console.WriteLine(ppl[i].ToString());

        foreach(var p in ppl)
            Console.WriteLine(p.ToString());
    }

}

exactly the same as when we implemented the PeopleEnumerator.

The takeaway here is that the yield keyword before our return statement basically lets us return an enumerator without having to manually implement one.