c# linq constructor

c# - ¿Es mejor usar Enumerable.Empty<T>() en comparación con la nueva Lista<T>() para inicializar un IEnumerable<T>?



linq constructor (7)

Supongamos que tiene una persona de clase:

public class Person { public string Name { get; set;} public IEnumerable<Role> Roles {get; set;} }

Obviamente debería instanciar los Roles en el constructor. Ahora, solía hacerlo con una lista como esta:

public Person() { Roles = new List<Role>(); }

Pero descubrí este método estático en el espacio de nombres System.Linq

IEnumerable<T> Enumerable.Empty<T>();

Desde MSDN :

El método Empty(TResult)() almacena en caché una secuencia vacía de tipo TResult . Cuando el objeto que devuelve se enumera, no produce elementos.

En algunos casos, este método es útil para pasar una secuencia vacía a un método definido por el usuario que toma un IEnumerable(T) . También se puede usar para generar un elemento neutral para métodos como Union . Consulte la sección Ejemplo para ver un ejemplo de este uso de

Entonces, ¿es mejor escribir el constructor así? ¿Lo usas? ¿Por qué? o si no, ¿por qué no?

public Person() { Roles = Enumerable.Empty<Role>(); }


Asumiendo que en realidad quiere poblar la propiedad Roles alguna manera, entonces encapsule eso haciendo que su setter sea privado e inicializándolo a una nueva lista en el constructor:

public class Person { public string Name { get; set; } public IList<Role> Roles { get; private set; } public Person() { Roles = new List<Role>(); } }

Si realmente desea tener el public setter, deje Roles con un valor null y evite la asignación de objetos.


Creo que Enumerable.Empty<T> es mejor porque es más explícito: tu código indica claramente tus intenciones. También podría ser un poco más eficiente, pero eso es solo una ventaja secundaria.


Creo que la mayoría de las publicaciones se perdieron el punto principal. Incluso si usa una matriz vacía o una lista vacía, esos son objetos y se almacenan en la memoria. Than Garbage Collector tiene que encargarse de ellos. Si se trata de una aplicación de alto rendimiento, podría tener un impacto notable.

Enumerable.Empty no crea un objeto por llamada, lo que genera menos carga en el GC.

Si el código está en una ubicación de bajo rendimiento, sin embargo se reduce a consideraciones estéticas.


El problema con su enfoque es que no puede agregar ningún elemento a la colección: tendría una lista de estructura privada y luego expongo los elementos como un enumerable:

public class Person { private IList<Role> _roles; public Person() { this._roles = new List<Role>(); } public string Name { get; set; } public void AddRole(Role role) { //implementation } public IEnumerable<Role> Roles { get { return this._roles.AsEnumerable(); } } }

Si tiene la intención de que otra clase cree la lista de roles (que no recomendaría), no inicializaría el enumerable en Persona.


El problema más grande aquí sería exponer Roles como un campo público .

el siguiente se ve mejor:

public class Person { public string Name { get; set; } private List<Role> _roles = null; public IEnumerable<Role> Roles { get { if (_roles != null) return _roles; else return Enumerable.Empty<Role>(); } } }

Y tal vez deberías echar un vistazo a devolverlo como ReadonlyCollection , dependiendo de cómo quieras usarlo.

Y Enumerable.Empty no es better aquí, solo un poco más eficiente cuando Roles generalmente permanece vacío.


El problema típico al exponer la Lista privada como un IEnumerable es que el cliente de tu clase puede meterse con ella mediante el casting. Este código funcionaría:

var p = new Person(); List<Role> roles = p.Roles as List<Role>; roles.Add(Role.Admin);

Puede evitar esto implementando un iterador:

public IEnumerable<Role> Roles { get { foreach (var role in mRoles) yield return role; } }


En el frente de rendimiento, veamos cómo se implementa Enumerable.Empty<T> .

Devuelve EmptyEnumerable<T>.Instance , que se define como:

internal class EmptyEnumerable<T> { public static readonly T[] Instance = new T[0]; }

Los campos estáticos en tipos genéricos se asignan por parámetro de tipo genérico. Esto significa que el tiempo de ejecución puede crear estas matrices vacías de forma perezosa solo para los tipos de código de usuario que necesita, y reutilizar las instancias tantas veces como sea necesario sin agregar ninguna presión sobre el recolector de basura.

Esto es:

Debug.Assert(ReferenceEquals(Enumerable.Empty<int>(), Enumerable.Empty<int>()));