How to Avoid Returning Null from a Method

by Zoran Horvat

The Problem of Returning Null

In so many occasions we have a method which might not be able to produce its result, given the argument values it received. And when saying not able to produce, I do not mean that execution has failed. Instead, method was executed successfully but the result is not any particular object that might have been expected. Simply put, the result is – null.

What do we do with the null result then? Obviously, in languages like C++, Java or C#, we cannot invoke any behavior on the result. Any such call would fail. Therefore, if we are dealing with the method that might occasionally return null, we have to guard from null. And the guard clause must be on the client.

Take a look at this example. Suppose that we have a caching component:

class Cache<T>
{

    private Dictionary<string, T> data = new Dictionary<string, T>();

    public void Store(string key, T value)
    {
        this.data[key] = value;
    }

    public T Get(string key)
    {
        if (this.data.ContainsKey(key))
            return this.data[key];
        return default(T);
    }

}

This cache keeps data indexed by keys, which are strings. The Store method can be used to associate a value with a desired key. On the other hand, Get method can be used to retrieve a cached item. The problem appears when Get is invoked on a cache which doesn’t contain the item with specified key value. In that case, Get just returns the default value for the item type T – in reference types that means null.

Let me demonstrate now what this means to use this class:

Cache<string> cache = new Cache<string>();
cache.Store("a", "car");
cache.Store("b", "truck");
cache.Store("c", "bus");

Console.WriteLine("My favorite vehicle is {0}", cache.Get("b"));

In this case, client is using the cache to pick a desired item and to print it out. The code just prints the favorite vehicle:

            
My favorite vehicle is truck
                
    

The fact that the Get method might return null doesn't mean much. That is simply due to the WriteLine method which will forgive us and just skip printing the value out. But the problem is still there, and we can make it appear if we attempt to invoke any behavior on the result:

Cache<string> cache = new Cache<string>();
cache.Store("a", "car");
cache.Store("b", "truck");
cache.Store("c", "bus");

Console.WriteLine("My favorite vehicle is {0}", cache.Get("b").ToUpper());

This time, the code still works, it only prints the vehicle in uppercase:

            
My favorite vehicle is TRUCK
                
    

But this code is easy to break. It is sufficient to pass a nonexistent key to the Get method:

Console.WriteLine("My favorite vehicle is {0}", cache.Get("d").ToUpper());

The key value "d" does not exist in the cache and Get method will just return null. What happens next is the call to the ToUpper method of the string object, and that method will fail with NullReferenceException:

            
Unhandled Exception: System.NullReferenceException:
Object reference not set to an instance of an object.
   at ConsoleApplication1.Program.Main(String[] args)
                
    

It is obvious that the client must guard from receiving null result back from the method call.

Guard Clause around Null

The simplest approach to dealing with null results is to place an if-then-else statement around them. Applied to previous example, it would look like this:

string vehicle = cache.Get("b");
if (vehicle == null)
    Console.WriteLine("No favorite vehicle");
else
    Console.WriteLine("My favorite vehicle is {0}", vehicle.ToUpper());

This statement fully protects the client from the null result. But it has one undesirable consequence – it is complicated. Every time we use the Get method, we must guard from the null result. Forget it just in one place, and there will be a defect hiding in the middle of your code, waiting for the null result to break its execution. Therefore, this solution is generally not acceptable in practice.

In the remainder of this article, we will visit several solutions to the problem. The goal will not be to give you a ready-made solution to all problems with null results. It is rather intended to give you options to choose from when designing a method which would occasionally have to return the null result.

Returning the Success Flag from the Method

One trivial solution to the problem is to return information whether the method succeeded or not. We can see that approach in the TryParse methods provided with numeric types, for example. Applied to the Get method of the Cache class, that would look something like this:

class Cache<T>
{
    ...
    public bool TryGet(string key, out T value)
    {

        if (this.data.ContainsKey(key))
        {
            value = this.data[key];
            return true;
        }

        value = default(T);
        return false;

    }
    ...
}

The TryGet method here returns True on success, in which case the output parameter value will contain the desired result. In case of a failure, method returns False and the output parameter should not be used.

Let's see what the client looks like in this case:

string vehicle;
if (cache.TryGet("b", out vehicle))
    Console.WriteLine("My favorite vehicle is {0}", vehicle.ToUpper());
else
    Console.WriteLine("No favorite vehicle");

Once again, we have an if-then-else statement which drives the client through the process. This solution may sound reasonable in many occasions. However, many developers feel discomfort having the TryX method signature with an output parameter. Output parameters are generally confusing the design.

One way out of the situation is to produce a separate structure with two elements – a Boolean flag indicating success and the actual result:

class ConditionalResult<T>
{

    public bool Success { get; private set; }
    public T Result { get; private set; }

    public ConditionalResult()
    {
        this.Success = false;
        this.Result = default(T);
    }

    public ConditionalResult(T value)
    {
        this.Success = true;
        this.Result = value;
    }

}

Now we can return this value from the Get method and forget about the TryGet:

class Cache<T>
{
    ...
    public ConditionalResult<T> Get(string key)
    {

        if (this.data.ContainsKey(key))
            return new ConditionalResult<T>(this.data[key]);

        return new ConditionalResult<T>();

    }
    ...
}

Client code is almost the same as it used to be before:

ConditionalResult<string> vehicle = cache.Get("b");
if (vehicle.Success)
    Console.WriteLine("My favorite vehicle is {0}", vehicle.Result.ToUpper());
else
    Console.WriteLine("No favorite vehicle");

If you like if-then-else logic, this is still the right way to go. The whole benefit from solution like this one is that the Get method never returns null. But success is only partial, because client may still receive a null value as the Result property of the conditional result object. Should the client fail to test the Success flag before accessing the Result, it would receive a NullReferenceException again.

In the following section I will provide a solution which derives from functional programming principles.

Returning the Maybe Object

Functional languages, such as Scala or F#, define a special type Option. Possible values of this type are either nothing or a single value. Option is used to store optional value, a value which might be missing. In C# such class is sometimes called Maybe. It can be implemented as a collection with zero or one elements. If collection is empty, the value is missing. If collection contains one element, that element is the desired value.

Here is one very simple implementation of the Maybe of T class:

public class Maybe<T> : IEnumerable<T>
{

    private readonly IEnumerable<T> values;

    public Maybe()
    {
        this.values = new T[0];
    }
    public Maybe(T value)
    {
        this.values = new T[] { value };
    }

    public IEnumerator<T> GetEnumerator()
    {
        return this.values.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

}

This class, similar to ConditionalResult<T> provided earlier, exposes two constructors. Default constructor produces an empty Maybe. Alternative constructor receives a value and creates a Maybe which contains that value.

Now we can modify the Get method of the cache class to return Maybe of string:

class Cache<T>
{
    ...
    public Maybe<T> Get(string key)
    {

        if (this.data.ContainsKey(key))
            return new Maybe<T>(this.data[key]);

        return new Maybe<T>();

    }
    ...
}

Logic of the Get method is the same as it used to be with the conditional result. But this time, the client can be written in a totally different manner. Get ready for this implementation:

string message =
    cache
    .Get("b")
    .Select(vehicle => string.Format("My favorite vehicle is {0}", vehicle.ToUpper()))
    .DefaultIfEmpty("No favorite vehicle")
    .Single();

Console.WriteLine(message);

In this use, we have employed LINQ to Objects to transform the cached item into a message that will be displayed to the user. Maybe<string> is a collection of strings, as it implements the IEnumerable<string>. In other words, LINQ to Objects is perfectly applicable to it. Therefore, we could transform the successfully fetched string into a user-friendly message, including the call to the ToUpper method. On the other hand, DefaultIfEmpty LINQ extension method will catch the case when nothing happened in the Select method, which occurs when Maybe contained no strings at all. That is the place to put the else branch of the if-then-else statement. Finally, we have to invoke the Single extension method to pull out the overall result of the operation.

This is the functional way of doing things. In many occasions, this approach is more favorable than good old if-then-else. In this case, DefaultIfEmpty method has been applied to serve the purpose of the alternate control flow. In some other case, we could use Any extension method to indicate success, or use Union method to conditionally add a value to another collection. There is a wide set of options before me, just because Maybe<T> implements IEnumerable<T> interface, which lets me use the whole of LINQ for further operations on the conditional result.

Extending the Maybe Approach to More Complex Uses

In some cases, DefaultIfempty LINQ method might not be the best choice. That happens in cases when the alternate operation must be calculated. Generally, the control flow is to try a simple operation and then, if it fails, invest into a more complicated approach.

Applied to the caching example, this means that the client might not be happy with the failure when item cannot be located in the cache. You may have noticed that cache was not really caching anything in examples listed this far. Real caching scenario is to ask the cache for an item, but if cache indicates that item is not stored in it, then to go to the data source and pick it from there. Of course, it would be polite to give cache a copy of the item, so that it can happily return it the next time we ask for the same item.

So the scenario looks more like this. We will use some repository which knows about all the vehicles that might exist. Then, the client would access the repository if cache doesn’t contain the desired item:

class VehicleRepository
{
    public string Get(string key)
    {
        string result = string.Empty;
        switch (key)
        {
            case "a": result = "car"; break;
            case "b": result = "truck"; break;
            case "c": result = "bus"; break;
        }
        return result;
    }
}

This is the simplest repository possible. The client will be able to use this repository to fetch any vehicle it desires. And here is the client implementation:

VehicleRepository repository = new VehicleRepository();
Cache<string> cache = new Cache<string>();

string key = "b";
Maybe<string> maybeVehicle = cache.Get(key);

string vehicle = null;
if (maybeVehicle.Any())
{
    vehicle = maybeVehicle.Single();
}
else
{
    vehicle = repository.Get(key);
    cache.Store(key, vehicle);
}

Console.WriteLine("My favorite vehicle is {0}", vehicle.ToUpper());

Really, this is quite some typing. Even more, the control flow is quite complicated, since this time we have to feed the item into the cache on cache miss. One better way to deal with the problem is to put the call to the repository into the DefaultIfEmpty method. But, we must ensure that the call to the repository is made lazily – i.e. only if it turns out that the value really has to be obtained from there:

VehicleRepository repository = new VehicleRepository();
Cache<string> cache = new Cache<string>();

string key = "b";

string message =
    cache
    .Get(key)
    .DefaultIfEmpty(() =>
        {
            string vehicle = repository.Get(key);
            cache.Store(key, vehicle);
            return vehicle;
        })
    .Select(vehicle => string.Format("My favorite vehicle is {0}", vehicle.ToUpper()))
    .Single();

Console.WriteLine(message);

It would be very nice if we could do it like this. DefaultIfEmpty method here receives a delegate, which will only be invoked if the collection on which DefaultIfEmpty is executed is really empty. Unfortunately, such overload of the DefaultIfEmpty does not exist. On the other hand, it is not hard to implement:

static class FunctionalDefaultIfEmpty
{
    public static IEnumerable<T> DefaultIfEmpty<T>(this IEnumerable<T> source, Func<T> lambda)
    {
        if (source.Any())
            return source;
        return new T[] { lambda() };
    }
}

Only a couple of lines of code to get there. Now the client works fine:

            
My favorite vehicle is TRUCK
                
    

Applying Lazy Evaluation

To be honest, we didn't really have to write the new DefaultIfEmpty implementation for this solution to work. Starting with .NET Framework 4, System namespace contains class Lazy<T>. This is another functional type available in C#. It lets us define a delegate which will be invoked only when value is requested for the first time. Now instead of passing the delegate, we can use the lazy string as the default value when cache doesn’t contain the requested item:

VehicleRepository repository = new VehicleRepository();
Cache<string> cache = new Cache<string>();

string key = "b";

string message =
    cache
    .Get(key)
    .Select(vehicle => new Lazy<string>(() => vehicle))
    .DefaultIfEmpty(new Lazy<string>(() =>
        {
            string vehicle = repository.Get(key);
            cache.Store(key, vehicle);
            return vehicle;
        }))
    .Select(lazyVehicle => string.Format("My favorite vehicle is {0}", lazyVehicle.Value.ToUpper()))
    .Single();

Console.WriteLine(message);

There are a couple of changes compared to previous solution. First and most important is that DefaultIfEmpty now receives the lazy string value. Actual value of this string will not be evaluated until its Value property is first time accessed for reading. In other words, the delegate passed to the Lazy constructor is not going to be executed if its result is not going to be used. And we know that it is not going to happen unless Get method of the cache component returned an empty Maybe.

Another difference is the mapping added just after the Get method is executed. This mapping is important because Get method returns collection of strings, while DefaultIfEmpty expects collection of lazy strings. Therefore, we had to convert a string contained in the Maybe object into lazy string before proceeding.

Finally, the last Select call works with the lazy string, rather than the simple string. The change is that, in order to access actual string located inside, we had to read the Value property on the lazy string.

So much about modifications introduced in the solution based on lazy strings. Once again, added overload of the DefaultIfEmpty method is not required anymore and we can freely remove the FunctionalDefaultIfEmpty utility class from the solution.

Summary

In this article we have tackled the problem of returning null from a method. The problem boils down to failing client’s expectations: if a method is supposed to return an object, then client should be free to assume that the object will be non-null whatever happens.

Several ways to tackle the issue have been presented. More ways exist and can be found in the literature and on the Internet. Whichever the way you choose, make sure to pay attention to client’s needs. Any good solution to the problem will actually make the client code easier to write and to understand.

Finally, for your convenience, below is the full source code of the console application which uses the repository and caching component with help of the Maybe class.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Demonstration
{

    class VehicleRepository
    {
        public string Get(string key)
        {
            string result = string.Empty;
            switch (key)
            {
                case "a": result = "car"; break;
                case "b": result = "truck"; break;
                case "c": result = "bus"; break;
            }
            return result;
        }
    }

    public class Maybe<T> : IEnumerable<T>
    {

        private readonly IEnumerable<T> values;

        public Maybe()
        {
            this.values = new T[0];
        }
        public Maybe(T value)
        {
            this.values = new T[] { value };
        }

        public IEnumerator<T> GetEnumerator()
        {
            return this.values.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

    }

    class Cache<T>
    {

        private Dictionary<string, T> data = new Dictionary<string, T>();

        public void Store(string key, T value)
        {
            this.data[key] = value;
        }

        public Maybe<T> Get(string key)
        {

            if (this.data.ContainsKey(key))
                return new Maybe<T>(this.data[key]);

            return new Maybe<T>();

        }
    }

    class Program
    {

        static void Main()
        {

            VehicleRepository repository = new VehicleRepository();
            Cache<string> cache = new Cache<string>();

            string key = "b";

            string message =
                cache
                .Get(key)
                .Select(vehicle => new Lazy<string>(() => vehicle))
                .DefaultIfEmpty(new Lazy<string>(() =>
                {
                    string vehicle = repository.Get(key);
                    cache.Store(key, vehicle);
                    return vehicle;
                }))
                .Select(lazyVehicle => string.Format("My favorite vehicle is {0}", lazyVehicle.Value.ToUpper()))
                .Single();

            Console.WriteLine(message);

            Console.Write("Press ENTER to exit... ");
            Console.ReadLine();

        }

    }

}

If you wish to learn more, please watch my latest video courses

About

Zoran Horvat

Zoran Horvat is the Principal Consultant at Coding Helmet, speaker and author of 100+ articles, and independent trainer on .NET technology stack. He can often be found speaking at conferences and user groups, promoting object-oriented and functional development style and clean coding practices and techniques that improve longevity of complex business applications.

  1. Pluralsight
  2. Udemy
  3. Twitter
  4. YouTube
  5. LinkedIn
  6. GitHub