Как создать пустой IReadOnlyCollection

Я создаю метод расширения для MultiValueDictionary для инкапсуляции частых проверок ContainsKey, и мне было интересно, что было лучшим способом создать пустой IReadOnlyCollection?.

То, что я использовал до сих пор, это new List<TValue>(0).AsReadOnly(), но должен быть лучший способ, эквивалент IEnumerable 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;
}

Ответ 1

EDIT: новый .Net 4.6 добавляет API для получения пустого массива: Array.Empty<T> и массивы реализуют IReadOnlyCollection<T>. Это также уменьшает выделение, поскольку оно создает экземпляр только один раз:

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

То, что я закончил, это подражать реализации Enumerable.Empty с помощью 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]); }
        }
    }
}

Использование:

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

Ответ 2

Я не думаю, что есть что-то вроде Enumerable.Empty для коллекций только для чтения, но:

  • List<T> уже реализует IReadOnlyCollection<T>, поэтому вы можете избежать выделения одного объекта, не вызывая AsReadOnly() и просто вместо этого списывая список. Это менее "безопасно" теоретически, но практически не имеет практического значения.

  • Кроме того, вы можете кэшировать возвращаемый ReadOnlyCollection, чтобы избежать любого размещения объекта (кроме кэшированного объекта).

Ответ 3

Насколько я знаю, нет встроенного способа (интересно знать, если таковое). При этом вы можете использовать следующее:

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

По желанию вы можете кэшировать результаты, поскольку это ReadOnlyCollection поверх пустого массива. Он всегда будет таким же, независимо от того, сколько у вас экземпляров.

Ответ 4

Как насчет того, что имеет похожий синтаксис для 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];
    }
}

Используется следующим образом:

IReadOnlyCollection<int> empty = ReadOnlyCollection.Empty<int>();

Ответ 5

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