Есть ли в C # синглтон с пустым списком?

В 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

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

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

В Java есть Collections.EMPTY_LIST неизменный синглтон - "хорошо типизировано" через _ 5_, который служит этой цели, хотя я не уверен, может ли аналогичная концепция работать в C #, потому что универсальные шаблоны обрабатываются по-разному.

Спасибо.


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


Ответы (7)


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

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

person Stilgar    schedule 19.12.2011
comment
Это очень важный момент! Возможно, это требует нового ТАК-вопроса? - person ; 19.12.2011
comment
Вы были на несколько секунд медленнее, а ссылки на документацию не было * finger shake * ;-) Но вы соглашаетесь с расширением комментария svicks. - person ; 19.12.2011
comment
Также другие ребята получили больше голосов, так что мы квиты :) Я бы отправил вопрос, но сейчас иду спать, и я не смогу следить за вопросом. Почему бы не выложить это самому? - person Stilgar; 19.12.2011
comment
Поскольку методу класса может потребоваться возврат List (коллекция, доступ к которой можно получить по индексу). Эти List можно только читать. И иногда вам нужно вернуть такой List только для чтения с нулевым элементом в нем. Таким образом, Collections.emptyList() метод. Вы не можете просто вернуть пустой Iterable, потому что реализованный интерфейс указывает, что метод возвращает список. Большая проблема в том, что у них нет интерфейса ImmutableList, который вы можете вернуть. Та же проблема существует и в .NET: любой IList вполне может быть доступен только для чтения. - person Laurent Bourgault-Roy; 30.11.2012
comment
Кстати, каждый массив - это IList ‹correspondingType› в C #, и он также будет выдан, когда вы попытаетесь добавить новые элементы. - person Grzenio; 27.01.2014
comment
функция shouldIUseList (readOnlyMethodsINeed) {return (setOfListMethods () - setOfIterableMethods ()). containsAny (readOnlyMethodsINeed); } - person Rodrigo Quesada; 23.09.2016
comment
Выбрасывание исключений по сравнению с интерфейсом только для чтения ~ = получение ошибки во время выполнения по сравнению с ее получением во время компиляции. Я определенно предпочел бы, чтобы спецификация Java определяла интерфейс только для чтения для списков, но, к сожалению, это не так. - person Rodrigo Quesada; 23.09.2016
comment
Для кого это может касаться: очень часто причиной того, что нужен какой-то список (а не IEnumerable<T>), является его свойство материализации (отсутствие отложенного выполнения) и наличие известного Count. В этом случае мы должны использовать IReadOnlyCollection<T>. Если также требуется индексированный доступ, есть IReadOnlyList<T>. - person Timo; 09.07.2018

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

person Jon    schedule 19.12.2011
comment
Ну, не точно. :) Поскольку список был запрошен, Array.Empty<T>() более точен, так как это IList<T>. У него есть радостная выгода, заключающаяся в том, что он также удовлетворяет IReadOnlyCollection<T>. Конечно, там, где достаточно IEnumerable<T> , идеально подходит Enumerable.Empty<T>(). - person Timo; 09.07.2018
comment
@Timo, этот вопрос и многие ответы, включая этот, были написаны примерно за 3 года до того, как Array.Empty<T> стал доступен. :) В любом случае, несмотря на заголовок, вопрос ясно дает понять, что IEnumerable<T> подходит для этой цели. - person Jon; 10.07.2018

Я думаю, вы ищете Enumerable.Empty<T>().

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

person svick    schedule 19.12.2011

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

System.Array.Empty<T>()

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

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

person ventiseis    schedule 15.05.2018
comment
Спасибо, что указали, что документация не соответствует анализаторам roslyn. Вы когда-нибудь открывали проблему на GitHub, чтобы исправить это? - person John Zabroski; 11.01.2021

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

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

  foreach(var x in xs.EmptyIfNull())
  {
    ...
  }
person Andrew Hanlon    schedule 19.12.2011

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
}
person user2023861    schedule 07.12.2015

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

person sjb-sjb    schedule 30.07.2017