That\'s kind of a general question (but I\'m using C#), what\'s the best way (best practice), do you return null or empty collection for a method that has a collection as a
Returning an empty collection is better in most cases.
The reason for that is convenience of implementation of the caller, consistent contract, and easier implementation.
If a method returns null to indicate empty result, the caller must implement a null checking adapter in addition to enumeration. This code is then duplicated in various callers, so why not to put this adapter inside the method so it could be reused.
A valid usage of null for IEnumerable might be an indication of absent result, or an operation failure, but in this case other techniques should be considered, such as throwing an exception.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace StackOverflow.EmptyCollectionUsageTests.Tests
{
///
/// Demonstrates different approaches for empty collection results.
///
class Container
{
///
/// Elements list.
/// Not initialized to an empty collection here for the purpose of demonstration of usage along with method.
///
private List elements;
///
/// Gets elements if any
///
/// Returns elements or empty collection.
public IEnumerable GetElements()
{
return elements ?? Enumerable.Empty();
}
///
/// Initializes the container with some results, if any.
///
public void Populate()
{
elements = new List();
}
///
/// Gets elements. Throws if not populated.
///
/// Returns of .
public IEnumerable GetElementsStrict()
{
if (elements == null)
{
throw new InvalidOperationException("You must call Populate before calling this method.");
}
return elements;
}
///
/// Gets elements, empty collection or nothing.
///
/// Returns of , with zero or more elements, or null in some cases.
public IEnumerable GetElementsInconvenientCareless()
{
return elements;
}
///
/// Gets elements or nothing.
///
/// Returns of , with elements, or null in case of empty collection.
/// We are lucky that elements is a List, otherwise enumeration would be needed.
public IEnumerable GetElementsInconvenientCarefull()
{
if (elements == null || elements.Count == 0)
{
return null;
}
return elements;
}
}
class Element
{
}
///
/// http://stackoverflow.com/questions/1969993/is-it-better-to-return-null-or-empty-collection/
///
class EmptyCollectionTests
{
private Container container;
[SetUp]
public void SetUp()
{
container = new Container();
}
///
/// Forgiving contract - caller does not have to implement null check in addition to enumeration.
///
[Test]
public void UseGetElements()
{
Assert.AreEqual(0, container.GetElements().Count());
}
///
/// Forget to and use strict method.
///
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void WrongUseOfStrictContract()
{
container.GetElementsStrict().Count();
}
///
/// Call and use strict method.
///
[Test]
public void CorrectUsaOfStrictContract()
{
container.Populate();
Assert.AreEqual(0, container.GetElementsStrict().Count());
}
///
/// Inconvenient contract - needs a local variable.
///
[Test]
public void CarefulUseOfCarelessMethod()
{
var elements = container.GetElementsInconvenientCareless();
Assert.AreEqual(0, elements == null ? 0 : elements.Count());
}
///
/// Inconvenient contract - duplicate call in order to use in context of an single expression.
///
[Test]
public void LameCarefulUseOfCarelessMethod()
{
Assert.AreEqual(0, container.GetElementsInconvenientCareless() == null ? 0 : container.GetElementsInconvenientCareless().Count());
}
[Test]
public void LuckyCarelessUseOfCarelessMethod()
{
// INIT
var praySomeoneCalledPopulateBefore = (Action)(()=>container.Populate());
praySomeoneCalledPopulateBefore();
// ACT //ASSERT
Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
}
///
/// Excercise because of null passed to
///
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void UnfortunateCarelessUseOfCarelessMethod()
{
Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
}
///
/// Demonstrates the client code flow relying on returning null for empty collection.
/// Exception is due to on an empty collection.
///
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void UnfortunateEducatedUseOfCarelessMethod()
{
container.Populate();
var elements = container.GetElementsInconvenientCareless();
if (elements == null)
{
Assert.Inconclusive();
}
Assert.IsNotNull(elements.First());
}
///
/// Demonstrates the client code is bloated a bit, to compensate for implementation 'cleverness'.
/// We can throw away the nullness result, because we don't know if the operation succeeded or not anyway.
/// We are unfortunate to create a new instance of an empty collection.
/// We might have already had one inside the implementation,
/// but it have been discarded then in an effort to return null for empty collection.
///
[Test]
public void EducatedUseOfCarefullMethod()
{
Assert.AreEqual(0, (container.GetElementsInconvenientCarefull() ?? Enumerable.Empty()).Count());
}
}
}