¿Existe un singleton de "Lista vacía" en C #?

80

En C # uso LINQ e IEnumerable un poco. Y todo está bien y bien (o al menos en su mayor parte).

Sin embargo, en muchos casos me encuentro que necesito un vacío IEnumerable<X>por defecto. Es decir, me gustaria

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

para trabajar sin necesidad de una verificación nula. Ahora, esto es lo que hago actualmente, dependiendo del contexto más amplio:

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

Ahora, aunque lo anterior está perfectamente bien para mí , es decir, si hay alguna "sobrecarga adicional" con la creación del objeto de matriz, simplemente no me importa , me preguntaba:

¿Hay singleton "IEnumerable / IList inmutable vacío" en C # /. NET? (E, incluso si no es así, ¿existe una forma "mejor" de manejar el caso descrito anteriormente?)

Java tiene Collections.EMPTY_LISTsingleton inmutable - "bien escrito" vía Collections.emptyList<T>()- que sirve para este propósito, aunque no estoy seguro de si un concepto similar podría funcionar en C # porque los genéricos se manejan de manera diferente.

Gracias.


fuente
Bueno, maldición :) Esto es lo que obtengo por enfocarme en List / IList y no Enumerable / IEnumerable, gracias a todos - votos por todos lados.
2
public static class Array<T> { public static readonly T[] Empty = new T[0]; }y se le puede llamar como: Array<string>.Empty. Pregunté al respecto aquí en CodeReview .
Şafak Gür

Respuestas:

98

Que está buscando Enumerable.Empty<T>().

En otras noticias, la lista vacía de Java apesta porque la interfaz List expone métodos para agregar elementos a la lista que arrojan excepciones.

Stilgar
fuente
¡Ese es un punto muy válido! ¿Quizás justifique una nueva pregunta SO?
1
Estuvo unos segundos lento y no hay enlace de documentación * movimiento de dedo * ;-) Pero acepto para expandir el comentario de svicks.
Además, los otros chicos obtuvieron más votos a favor, así que estamos empatados :) Publicaría una pregunta, pero me voy a la cama ahora mismo y no podré seguir la pregunta. ¿Por qué no publicarlo usted mismo?
Stilgar
32
Debido a que un método de clase puede necesitar devolver un List(una colección a la que se puede acceder por índice). Esos Listpueden ser de solo lectura. Y en algún momento debe devolver un solo lectura Listcon un elemento cero. Así, el Collections.emptyList()método. No puede simplemente devolver un Iterable vacío porque una interfaz implementada especifica que el método devuelve una lista. El gran problema es que no hay una interfaz ImmutableList que pueda devolver. El mismo problema existe en .NET: cualquiera IListpuede ser de solo lectura.
Laurent Bourgault-Roy
6
En una nota al margen, cada matriz es un IList <correspondingType> en C #, y esto también se lanzará cuando intente agregar nuevos elementos.
Grzenio
52

Enumerable.Empty<T>() es exactamente eso.

Jon
fuente
3
Bueno, no exactamente . :) Dado que se solicitó una "lista", Array.Empty<T>()es más precisa, ya que es un IList<T>. Tiene la feliz ventaja de ser también satisfactorio IReadOnlyCollection<T>. Por supuesto, donde IEnumerable<T> sí es suficiente, Enumerable.Empty<T>()encaja perfectamente.
Timo
3
@Timo esta pregunta, y muchas de las respuestas, incluida esta, fueron escritas aproximadamente 3 años antes de que Array.Empty<T>estuviera disponible. :) En cualquier caso, a pesar del título, la pregunta deja explícitamente claro que un IEnumerable<T>es bueno para el propósito.
Jon
19

Creo que estás buscando Enumerable.Empty<T>().

Singleton de lista vacía no tiene mucho sentido, porque las listas a menudo son mutables.

svick
fuente
13

En su ejemplo original, usa una matriz vacía para proporcionar un enumerable vacío. Si bien usar Enumerable.Empty<T>()es perfectamente correcto, puede haber otros casos: si tiene que usar una matriz (o la IList<T>interfaz), puede usar el método

System.Array.Empty<T>()

lo que le ayuda a evitar asignaciones innecesarias.

Notas / referencias:

ventiseis
fuente
10

Creo que agregar un método de extensión es una alternativa limpia gracias a su capacidad para manejar nulos, algo como:

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

  foreach(var x in xs.EmptyIfNull())
  {
    ...
  }
Andrew Hanlon
fuente
1

Microsoft implementó 'Any ()' así ( fuente )

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

Si desea guardar una llamada en la pila de llamadas, en lugar de escribir un método de extensión que llame !Any(), simplemente vuelva a escribir y realice estos tres cambios:

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
fuente
1

Usar Enumerable.Empty<T>()con listas tiene un inconveniente. Si realiza la mano Enumerable.Empty<T>en el constructor de la lista, se asigna una matriz de tamaño 4. Pero si entrega un vacío Collectionen el constructor de la lista, no se produce ninguna asignación. Entonces, si usa esta solución en todo su código, lo más probable es que uno de los IEnumerables se use para construir una lista, lo que resultará en asignaciones innecesarias.

sjb-sjb
fuente