30

I'm creating an extension method for MultiValueDictionary to encapsulate frequent ContainsKey checks and I was wondering what was the best way to create an empty IReadOnlyCollection?.

What I've used so far is new List<TValue>(0).AsReadOnly() but there must be a better way, an equivilant to IEnumerable's Enumerable.Empty

public static IReadOnlyCollection<TValue> GetValuesOrEmpty<TKey, TValue>(this MultiValueDictionary<TKey, TValue> multiValueDictionary, TKey key)
{            
    IReadOnlyCollection<TValue> values;
    return !multiValueDictionary.TryGetValue(key, out values) ? new List<TValue>(0).AsReadOnly() : values;
}
i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • Not really, since `IReadOnlyCollection` is meant to be just a wrapper. You can consider returning an `IEnumerable`, or generally that does not have `Add` and `Remove` methods. – Marcel N. Aug 10 '14 at 09:56

5 Answers5

41

EDIT: The new .Net 4.6 adds an API to get an empty array: Array.Empty<T> and arrays implement IReadOnlyCollection<T>. This also reduces allocations as it only creates an instance once:

IReadOnlyCollection<int> emptyReadOnlyCollection = Array.Empty<int>();

What I ended up doing is mimicking the implementation of Enumerable.Empty using new TElement[0]:

public static class ReadOnlyCollection
{
    public static IReadOnlyCollection<TResult> Empty<TResult>()
    {
        return EmptyReadOnlyCollection<TResult>.Instance;
    }

    private static class EmptyReadOnlyCollection<TElement>
    {
        static volatile TElement[] _instance;

        public static IReadOnlyCollection<TElement> Instance
        {
            get { return _instance ?? (_instance = new TElement[0]); }
        }
    }
}

Usage:

IReadOnlyCollection<int> emptyReadOnlyCollection = ReadOnlyCollection.Empty<int>();
i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • 1
    Looks, good. But using `volatile` correctly is hard (here it is okay), but i'd like to stick to higher level apis and would pick a `private static Lazy> _EmptyCollection = new Lazy>(()=> new TElement[0]);` and within your Empty() method then take `return _EmptyCollection.Value;` – Oliver Nov 21 '14 at 15:00
6

return new List<XElement>().AsReadOnly();

Doug
  • 5,268
  • 24
  • 31
4

I don't think there's anything like Enumerable.Empty for read-only collections, but:

  • List<T> already implements IReadOnlyCollection<T> so you can avoid one object allocation by not calling AsReadOnly() and simply casting the list instead. This is less "safe" in theory but hardly matters in practice.

  • Alternatively, you could cache the returned ReadOnlyCollection to avoid any object allocation whatsoever (except for the cached object).

Victor Efimov
  • 384
  • 2
  • 5
  • Went with a combination of both answers and used `return new TValue[0]` – i3arnon Aug 10 '14 at 15:34
  • @I3arnon: If you give your class a `private static IReadOnlyCollection _EmptyCollection = new TValue[0]`, you only have to instantiate this empty array once instead whenever no value is available. Taking this a step further you could even pack this into a `Lazy` which would create this empty array only when it is needed for the first time. – Oliver Nov 21 '14 at 14:36
  • Nit: There is no need to even "cast" an expression of `List` as `List` (but not `IList`) implements `IReadOnlyCollection`. – user2864740 Jan 04 '18 at 20:09
4

As far as I know there is no built in way(Interested to know if one). That said, you can use the following:

IReadOnlyCollection<TValue> readonlyCollection = new ReadOnlyCollection<TValue>(new TValue[] { });

Optionally you can cache the results as it is a ReadOnlyCollection over empty array, It will always be the same no matter how many instances you have.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
2

How's about this which has a similar syntax to Enumerable.Empty:

/// <summary>
/// Contains a method used to provide an empty, read-only collection.
/// </summary>
public static class ReadOnlyCollection
{
    /// <summary>
    /// Returns an empty, read-only collection that has the specified type argument.
    /// </summary>
    /// <typeparam name="T">
    /// The type to assign to the type parameter of the returned generic read-only collection.
    /// </typeparam>
    /// <returns>
    /// An empty, read-only collection whose type argument is T.
    /// </returns>
    public static IReadOnlyCollection<T> Empty<T>()
    {
        return CachedValueProvider<T>.Value;
    }

    /// <summary/>
    static class CachedValueProvider<T>
    {
        /// <summary/>
        public static readonly IReadOnlyCollection<T> Value = new T[0];
    }
}

Used like this:

IReadOnlyCollection<int> empty = ReadOnlyCollection.Empty<int>();
Steven Rands
  • 5,160
  • 3
  • 27
  • 56