Есть ли в C # синглтон «Пустой список»?

80

В C # я хорошо использую LINQ и IEnumerable. И все хорошо (по крайней мере, в большинстве случаев).

Однако во многих случаях я считаю, что мне нужно пустое поле IEnumerable<X>по умолчанию. То есть я бы хотел

for (var x in xs) { ... }

чтобы работать без проверки нуля. Вот что я сейчас делаю, в зависимости от более широкого контекста:

var xs = f() ?? new X[0];              // when xs is assigned, sometimes
for (var x in xs ?? new X[0]) { ... }  // inline, sometimes

Теперь, хотя все вышесказанное для меня совершенно нормально - то есть, если есть какие-то «дополнительные накладные расходы» при создании объекта массива, мне просто все равно - мне было интересно:

Есть ли в C # /. NET "пустой неизменяемый синглтон IEnumerable / IList"? (И даже если нет, есть ли «лучший» способ справиться с описанным выше случаем?)

В Java есть Collections.EMPTY_LISTнеизменяемый синглтон - «хорошо типизированный» переход, Collections.emptyList<T>()который служит этой цели, хотя я не уверен, может ли аналогичная концепция вообще работать в C #, потому что универсальные шаблоны обрабатываются по-разному.

Благодарю.


источник
Ну, черт :) Это то, что я получаю за то, что сосредоточился на List / IList, а не на Enumerable / IEnumerable, спасибо всем - голоса всех вокруг.
2
public static class Array<T> { public static readonly T[] Empty = new T[0]; }и это можно назвать как: Array<string>.Empty. Я спросил об этом здесь, в CodeReview .
afak Gür

Ответы:

98

Вы ищите Enumerable.Empty<T>().

К другим новостям, пустой список Java - отстой, потому что интерфейс List предоставляет методы для добавления элементов в список, которые вызывают исключения.

Стилгар
источник
Это очень верный момент! Возможно, это требует нового ТАК-вопроса?
1
Вы были на несколько секунд медленнее, а ссылки на документацию не было * дрожание пальца * ;-) Но вы соглашаетесь с расширением комментария svicks.
Также другие ребята получили больше голосов, так что мы квиты :) Я бы отправил вопрос, но сейчас иду спать, и я не смогу следить за вопросом. Почему бы не выложить это самому?
Стилгар
32
Поскольку методу класса может потребоваться вернуть List(коллекцию, доступ к которой можно получить по индексу). Те Listмогут быть неизменяемые. А иногда нужно вернуть такой readonly Listс нулевым элементом. Итак, Collections.emptyList()метод. Вы не можете просто вернуть пустой Iterable, потому что реализованный интерфейс указывает, что метод возвращает список. Большая проблема в том, что у них нет интерфейса ImmutableList, который вы можете вернуть. Та же проблема существует и в .NET: любой IListможет быть доступен только для чтения.
Laurent Bourgault-Roy
6
Кстати, каждый массив - это IList <correspondingType> в C #, и он также будет выдан, когда вы попытаетесь добавить новые элементы.
Grzenio
52

Enumerable.Empty<T>() именно это.

Джон
источник
3
Не совсем так . :) Раз Array.Empty<T>()уж просили "список", точнее, так как это IList<T>. Это приносит радость, так как приносит удовлетворение IReadOnlyCollection<T>. Конечно, где IEnumerable<T> делает хватает, Enumerable.Empty<T>()идеально подходит.
Timo
3
@Timo, этот вопрос и многие ответы, включая этот, были написаны примерно за 3 года до того, как Array.Empty<T>стали доступны. :) В любом случае, несмотря на заголовок, вопрос ясно дает понять, что an IEnumerable<T>подходит для этой цели.
Джон
19

Думаю, ты ищешь Enumerable.Empty<T>().

Синглтон с пустым списком не имеет особого смысла, потому что списки часто изменяемы.

Свик
источник
13

В исходном примере вы используете пустой массив для предоставления пустого перечислимого. Хотя использование Enumerable.Empty<T>()совершенно правильно, могут быть и другие случаи: если вам нужно использовать массив (или IList<T>интерфейс), вы можете использовать метод

System.Array.Empty<T>()

что помогает избежать ненужных выделений.

Примечания / ссылки:

Veniseis
источник
10

Я думаю, что добавление метода расширения - это чистая альтернатива благодаря их способности обрабатывать нули - что-то вроде:

  public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> list)
  {
    return list ?? Enumerable.Empty<T>();
  }

  foreach(var x in xs.EmptyIfNull())
  {
    ...
  }
Эндрю Хэнлон
источник
1

Microsoft реализовала `Any () 'следующим образом ( источник )

public static bool Any<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw new ArgumentNullException("source");
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        if (e.MoveNext()) return true;
    }
    return false;
}

Если вы хотите сохранить вызов в стеке вызовов, вместо того, чтобы писать вызывающий метод расширения !Any(), просто перезапишите эти три изменения:

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source) //first change (name)
{
    if (source == null) throw new ArgumentNullException("source");
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        if (e.MoveNext()) return false; //second change
    }
    return true; //third change
}
user2023861
источник
1

Использование Enumerable.Empty<T>()со списками имеет недостаток. Если вы перейдете Enumerable.Empty<T>в конструктор списка, то будет выделен массив размером 4. Но если вы передаете пустое значение Collectionв конструктор списка, распределения не происходит. Поэтому, если вы используете это решение во всем своем коде, то, скорее всего, IEnumerableдля создания списка будет использоваться один из s, что приведет к ненужным выделениям.

sjb-sjb
источник