Friday, September 28, 2012

NonNull extension method

One of the things that drives me nuts lately is null checking, my code is plagued with:
if (foo != null)
{
    // Business as usual
}

Say you have the following classes (exaggerated, the property "First" in class "Name" would usually just be a string):
public class Person
{
    public Name Name { get; set; }
}

public class Name
{
    public First First { get; set; }
}

public class First
{
    public string Value { get; set; }
}

And the following collection:
var people = new[]
{
    new Person { Name = new Name { First = new First { Value = "John" } } },
    new Person { Name = new Name { First = new First { Value = "Eric" } } },
    new Person { Name = new Name { First = new First { Value = "Joel" } } },
    new Person { Name = new Name { First = new First { Value = null } } },
    new Person { Name = new Name { First = new First() } },
    new Person { Name = new Name() },
    new Person { Name = null },
    new Person(),
    null
};

On a given query, you might see something like:
private void PrintFirstNames(IEnumerable<Person> people)
{
    if (people != null)
    {
        foreach (var person in people)
        {
            if (person != null && person.Name != null
                    && person.Name.First != null && person.Name.First.Value != null)
            {
                Console.WriteLine(person.Name.First.Value);
            }
        }
    }
}

Which can, of course, be written more concisely in LINQ:
private void PrintFirstNamesLinq(IEnumerable<Person> people)
{
    var names = people.Where(p => p != null).Select(p => p.Name)
                .Where(n => n != null).Select(n => n.First)
                .Where(f => f != null).Select(f => f.Value);
    foreach (var name in names)
    {
        Console.WriteLine(name);
    }
}

However, after writing a handy little extension method called NonNull:
public static IEnumerable<R> NonNull<T, R>(this IEnumerable<T> source, Func<T, R> action)
{
    if (source == null)
    {
        yield break;
    }

    foreach (var val in source.Where(s => s != null).Select(action).Where(val => val != null))
    {
        yield return val;
    }
}

It can be written even more concisely:
private void PrintFirstNamesNonNull(IEnumerable<Person> people)
{
    var names = people.NonNull(p => p).NonNull(p => p.Name).NonNull(p => p.First).NonNull(p => p.Value);
    foreach (var name in names)
    {
        Console.WriteLine(name);
    }
}

And even more concisely with a ForEach extension method (see here for code and the debate surrounding it):
private void PrintFirstNamesNonNullForEach(IEnumerable<Person> people)
{
    people.NonNull(p => p).NonNull(p => p.Name).NonNull(p => p.First).NonNull(p => p.Value).ForEach(Console.WriteLine);
}
Another win for C# extension methods (and Monads, but I suppose that's another discussion).

No comments:

Post a Comment