Null Handling with Extension Methods

Often we cannot be sure if a parameter passed in to a function has a value. To write error safe code all those parameters have to be checked for null and handled. We have the ?? coalesce operator to help, but still it can be quite a lot of code. Through the use of extension methods a lot of cases can be handled. As extension methods are in reality static methods of another class, they work even if the reference is null. This can be utilized to write utility methods for various cases. In this post I’ll show two cases, one is an extension that returns a default value for an XML attribute value if the attribute or even the element is missing. The other one handles the common case when a null sequence reference passed should be handled as an empty sequence.

Xml GetValueOrDefault() and GetAttributeValueOrDefault()

One case where extension methods can simplify code is retrieving attribute values when using LINQ to XML. If I have a default value for an attribute in an Xml configuration file I would just like to get the value if there is such an attribute. If the attribute or even the entire element is missing I would like a default value. Unfortunately I first have to check if the element is present, then check the attributes presence and finally get the value.

public static string GetServerName(XElement config)
{
    XElement element = config.Element("database");
    string serverName = "(local)";
    if (element != null)
    {
        XAttribute server = element.Attribute("server");
        if (server != null)
        {
            serverName = server.Value;
        }
    }
    return serverName;
}

With a helper method this can be simplified a lot.

public static string GetServerName2(XElement config)
{
    return config.Element("database")
        .GetAttributeValueOrDefault("server", "(local)");
}

The helper is divided into two functions, one that can accept a null attribute and one that can accept a null element.

/// <summary>
/// Returns the value if attribute is not null, otherwise the specified
/// default string.
/// </summary>
/// <param name="attribute">Attribute whos value should be returned if
/// <paramref name="attribute"/> is not null.</param>
/// <param name="defaultValue">Value to return if <paramref name="attribute"/>
/// is null.</param>
/// <returns>String value.</returns>
public static string GetValueOrDefault(this XAttribute attribute, 
    string defaultValue = null)
{
    if (attribute == null)
        return defaultValue;
    else
        return attribute.Value;
}
 
/// <summary>
/// Returns the attribute value if the element is not null, and the element
/// has the specified attribute, otherwise the default string.
/// </summary>
/// <param name="element">XML element with attribute. May be null.</param>
/// <param name="attributeName">Name of attribute.</param>
/// <param name="defaultValue">default value.</param>
/// <returns>String value.</returns>
public static string GetAttributeValueOrDefault(this XElement element,
    string attributeName, string defaultValue = null)
{
    if (element == null)
        return defaultValue;
    else
        return element.Attribute(attributeName).GetValueOrDefault(defaultValue);
}

EmptyIfNull()

The other example converts a null reference to an IEnumerable<T> into an empty sequence. It can be used in a function that accepts a sequence that might be null.

public static void PrepareEach<T>(IEnumerable<T> sequence)
{
    if (sequence != null)
    {
        foreach (T t in sequence)
        {
            Prepare(t);
        }
    }
}

It’s not much code, but I think that each if statement and indented block requires some extra effort to grasp when reading the code, so I’ll introduce an extension method to help.

public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> sequence)
{
    return sequence ?? Enumerable.Empty<T>();
}
 
public static void PrepareEach<T>(IEnumerable<T> sequence)
{
    foreach (T t in sequence.EmptyIfNull())
    {
        Prepare(t);
    }
}

The second version of PerpareEach doesn’t save that much code compared to the first one, but it simplifies the structure of the function, making it more intuitive to understand.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.