Чи є в 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 .
faafak Gür

Відповіді:


98

Ви шукаєте Enumerable.Empty<T>().

В інших новинах порожній список Java відстійний, оскільки інтерфейс List пропонує методи додавання елементів до списку, які видають винятки.


Це дуже вагомий пункт! Можливо, це вимагає нового SO-питання?

1
Ви працювали кілька секунд повільно, і жодного посилання на документацію * тремтіння пальцем * ;-) Але прийняти для розширення після коментаря svicks.

Крім того, інші хлопці отримали більше голосів, тому ми навіть :) Я б опублікував запитання, але я зараз лягаю спати, і я не зможу стежити за цим питанням. Чому б не опублікувати це самостійно?
Stilgar

32
Оскільки методу класу може знадобитися повернути a List(колекцію, до якої можна отримати доступ за допомогою індексу). Їх Listможна прочитати лише для читання. І іноді вам потрібно повернути таке читання лише Listз нульовим елементом. Таким чином, Collections.emptyList()спосіб. Ви не можете просто повернути порожній Iterable, оскільки реалізований інтерфейс вказує, що метод повертає список. Велика проблема полягає в тому, що їх немає ніякого інтерфейсу ImmutableList, який ви можете повернути. Така сама проблема існує і в .NET: будь-яка IListцілком може бути лише для читання.
Лоран Бурго-Рой

6
Як примітка, кожен масив є IList <correspondingType> у C #, і це буде видаватися при спробі також додати нові елементи.
Grzenio

52

Enumerable.Empty<T>() саме це.


3
Ну, не зовсім так . :) Оскільки запитували "список", він Array.Empty<T>()є більш точним, як і IList<T>. Він має щасливу користь, а також задовольняти IReadOnlyCollection<T>. Звичайно, де IEnumerable<T> робить вистачає, Enumerable.Empty<T>()ідеально підходить.
Тимо

3
@Timo це питання та багато відповідей, включаючи це, були написані приблизно за 3 роки до того, як Array.Empty<T>було доступно. :) У будь-якому випадку, незважаючи на заголовок, питання чітко дає зрозуміти, що an IEnumerable<T>добре підходить для цієї мети.
Джон

19

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

Порожній одиночний список не має такого сенсу, оскільки списки часто змінюються.


13

У оригінальному прикладі ви використовуєте порожній масив, щоб надати порожній перелік. Незважаючи на те, що використання Enumerable.Empty<T>()абсолютно правильне, можуть бути й інші випадки: якщо вам потрібно використовувати масив (або IList<T>інтерфейс), ви можете використовувати метод

System.Array.Empty<T>()

що допомагає уникнути зайвих розподілів.

Примітки / посилання:


10

Я думаю, що додавання методу розширення є чистою альтернативою завдяки їх здатності обробляти нулі - щось на зразок:

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

  foreach(var x in xs.EmptyIfNull())
  {
    ...
  }

1

Корпорація Майкрософт реалізувала `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
}

1

Використання Enumerable.Empty<T>()зі списками має недолік. Якщо ви здасте Enumerable.Empty<T>конструктор списку, тоді виділяється масив розміром 4. Але якщо ви передаєте порожнє Collectionв конструктор списку, то виділення не відбувається. Отже, якщо ви використовуєте це рішення у коді, то, швидше за все, один із IEnumerables буде використаний для побудови списку, що призведе до непотрібних розподілів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.