Есть ли в C # синглтон "Пустой список"? - PullRequest
63 голосов
/ 19 декабря 2011

В 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

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

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

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

Спасибо.

Ответы [ 7 ]

81 голосов
/ 19 декабря 2011

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

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

49 голосов
/ 19 декабря 2011

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

18 голосов
/ 19 декабря 2011

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

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

10 голосов
/ 20 декабря 2011

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

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

  foreach(var x in xs.EmptyIfNull())
  {
    ...
  }
5 голосов
/ 15 мая 2018

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

System.Array.Empty<T>()

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

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

1 голос
/ 30 июля 2017

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

1 голос
/ 07 декабря 2015

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

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
}
...