Using the yield keyword in C#

The yield contextual keyword was first introduced in C# 2.0 and despite that fact, it is rarely used. Perhaps it is not a well-known feature or perhaps it is misunderstood but it has plenty of uses. In this post, I will demonstrate how to use the yield contextual keyword and discuss some common scenarios where it would be useful.

yield

What does the yield keyword do?

The yield contextual keyword returns an IEnumerator<T> collection and returns the items in that collection one at a time. If that seems confusing, think of a method that returns a list of items. Yield will allow you to return an IEnumerator<T> collection without declaring the collection.

Listing 1.1 – ContactValidator.cs

public static class ContactValidator
{
    public static IEnumerable<string> GetErrors(Contact contact)
    {
        if (string.IsNullOrEmpty(contact.LastName))
        {
            yield return "Last Name is required.";
        }

        if(string.IsNullOrEmpty(contact.EmailAddress))
        {
            yield return "Email Address is required.";
        }

        if(string.IsNullOrEmpty(contact.PhoneNumber))
        {
            yield return "Phone Number is required.";
        }

        if(contact.ID == Guid.Empty)
        {
            yield return "ID is required.";
        }
    }
}

Ignore that the ContactValidator class and GetErrors method are static. It doesn’t matter and it doesn’t have to be. The method is straight-forward. It is checking the values of several different properties and if each of those properties does not meet requirements, an error message is returned.

Listing 1.2 – Contact.cs

public class Contact
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }
    public Guid ID { get; set; }
    public string PhoneNumber { get; set; }
}

Listing 1.3 – Testing the yield functionality

static void Main(string[] args)
{
    Contact contact = new Contact
    {
        FirstName = "Ted"
    };
            
    List<string> errors = ContactValidator.GetErrors(contact).ToList();

    foreach(string err in errors)
    {
        Console.WriteLine(err);
    }

    Console.ReadKey();
}

This will create a new contact that has all the required and validated fields missing. Looking at the validation code in Listing 1.1, what are the expected results?

Listing 1.4 – ContactValidator results

Last Name is required.
Email Address is required.
Phone Number is required.
ID is required.

Yield allows this by returning the error messages one at a time as they occur but only returning after the method has completed.

One more quick example.

Listing 1.5 – Use of yield break

public IEnumerable<int> GetMultiplesOfSix()
{
    for(int i = 0; i < 100; i++)
    {
        if(i%6 == 0)
        {
            yield return i;
        }

        if(i > 48)
        {
            yield break;
        }
    }
}

In this example, we are using yield inside of a loop. If the value of i is divisible by six, it is being returned. However, if i is greater than 48, we are exiting the loop through the use of yield break.

Listing 1.6 – Output

0
6
12
18
24
30
36
42
48

Conclusion

Yield offers a different way of doing things but I actually like using it in my code. It means less code because I don’t have to create my own collection or manage it and it makes for a bit cleaner code. However, because it isn’t widely used or even known, I am careful in its application.

Leave a Reply

Your email address will not be published.