2008-12-05 12 views
10

¿Tiene dot net una interfaz como IEnumerable con una propiedad count? Sé sobre interfaces como IList e ICollection que sí ofrecen una propiedad Count pero parece que estas interfaces fueron diseñadas para estructuras de datos mutables primero y su uso como interfaz de solo lectura parece una ocurrencia tardía: la presencia de un campo IsReadOnly y mutadores lanzando excepciones cuando esta propiedad es cierta, hay una amplia evidencia de la OMI para esto.¿Tiene dot net una interfaz como IEnumerable con una propiedad Count?

Por el momento estoy usando una interfaz personalizada llamada IReadOnlyCollection (vea mi propia respuesta a esta publicación) pero me gustaría saber de otros enfoques alternativos.

+0

No creo que IEnumerator sea la elección correcta si necesita una propiedad Count. Un enumerador hace exactamente eso, enumera cosas y es agnóstico sobre cuántos elementos hay. Los enumeradores no están limitados a enumerar colecciones finitas, piense en la declaración de rentabilidad en C# – Trap

+0

@Trap Acepto que IEnumerator/IEnumerable por sí solo no se puede usar para representar una colección finita, pero seguramente se puede usar como base para un IList y ICollection por ejemplo. Entonces, lo que quiero es IEnumerable + Count property = IReadOnlyCollection. –

Respuesta

14

La diferencia clave entre la familia ICollection y la familia IEnumerable es la falta de certeza en cuanto al recuento de elementos presentes (con bastante frecuencia los elementos se generarán/cargarán/hidratarán según sea necesario) - en algunos casos, un Enumerable nunca termine de generar resultados, razón por la cual el Conde está perdido.

Derivar y agregar un conteo es posible dependiendo de sus requisitos, pero va en contra de este espíritu, que es el propósito de ICollection: una colección de cosas que está todo allí.

Otra forma podría ser usar el método System.Linq.Enumerable.Count, es decir

using System.Linq; 

class X 
{ 
    void Y(IEnumerable<int> collection) 
    { 
     int itemCount = collection.Count(); 
    } 
} 

o utilizar el (System.Linq.Enumerable) .ToList() para tirar de todos los elementos de la empadronador en una Colección y trabaja desde allí.

(También para responder a su comentario antes de tener 50 rep: - el bit ".Count()" es una llamada a un método de extensión en la clase de extensión System.Linq.Enumerable - el método de extensión está disponible en todo lo que deriva de IEnumerable porque el código tiene un "using System.Linq" que pone los métodos de extensión en todas las clases en ese espacio de nombres en el alcance, en este caso está en la clase Enumerable. Si estás en VS, presionando F12 te llevará a . la definición de SLEnumerable BTW C# En Profundidad es un libro fantástico para el aprendizaje de LINQ adecuadamente - es una página que eso es turner realmente le ayuda a obtener toda la imagen en comparación con el aprendizaje de los bits de LINQ pieza por pieza)

+0

¿Cómo funciona "itemCount = collection.Count()"? IEnumerable no tiene una propiedad Count. –

+0

No, tiene métodos de extensión Count() (a partir de .NET 3.5). Count() usa la propiedad Count si la colección es un IList, o recorre toda la colección y cuenta los elementos de otra manera. –

+0

@Ruben: Gracias por el voto de confianza en el libro :) –

2

IList puede devolver IsReadOnly como verdadero, que marca la colección como de solo lectura. Aparte de eso, me temo que no sé nada apropiado.

+0

Sí, acabo de ver eso, pero ¿cómo puedo cambiar una Lista en un IList tal que IsReadOnly es cierto? –

+0

Normalmente devolverá un objeto contenedor implementando IList o lo que necesite, que hace referencia interna a su lista original. –

+1

Puede utilizar el método List .AsReadOnly para devolver un contenedor de solo lectura para su lista. –

0

Puede obtener .Count en IEnumerable con un método de extensión si agrega una referencia a System.Linq (en 3.5 de todos modos).

+0

Esto va a ser una operación O (n), que bien podría no ser lo que se quería ... –

+1

Es una operación O (n) si la colección subyacente no implementa IList, pero si lo hace, simplemente usa la propiedad IList.Count. –

+0

@Jon gracias por la información pero no quiero exponer un IList ya que quiero que los datos sean de solo lectura. –

2

Dado que es una interfaz, usted debería implementar la propiedad Count usted mismo, ¿por qué no crea una nueva interfaz que hereda IEnumerator y agrega una propiedad Count?

+0

Acepto que IEnumeratorEx (ver mi propia respuesta) es exactamente lo que quiero. IList/ICollection con la mitad de los métodos que arrojan excepciones parece un poco desagradable para un programador profesional de "mecanografía fuerte" como yo. –

+0

Ok IEnumeratorEx ahora es IReadOnlyCollection. –

1

IList o ICollection haría sé el camino a seguir, si quieres tú se las interfaces estándar.

Observe que puede métodos de "ocultar" requerido por la interfaz si no quiere que en la interfaz pública de su clase - por ejemplo, ya no tiene sentido añadir cosas a una colección de sólo lectura se puede hacer esto:

void ICollection<DataType>.Add(DataType item) 
{ 
    throw new NotSupportedException(); 
} 

public DataType this[int index] 
{ 
    get { return InnerList[index]; } 
} 

DataType IList<DataType>.this[int index] 
{ 
    get { return this[index]; } 
    set { throw new NotSupportedException(); } 
} 

etc.

4

Tomando en consideración algunos de los comentarios que he decidido ir con una clase contenedora implementar una interfaz personalizada ...

interface IReadOnlyCollection<T> : IEnumerable<T> 
{ 
    int Count { get; } 
} 

//This can now be not misused by downcasting to List 
//The wrapper can also be used with lists since IList inherits from ICollection 
public class CollectionWrapper<T> : IReadOnlyCollection<T> 
{ 

    public CollectionWrapper(ICollection<T> collection) 
    { 
     _collection = collection; 
    } 

    public int Count 
    { 
     get 
     { 
      return _collection.Count; 
     } 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return (IEnumerator<T>)_collection.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return (IEnumerator)((IEnumerable)_collection).GetEnumerator(); 
    } 

    ////////Private data/////// 
    ICollection<T> _collection; 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     List<int> list = new List<int>(); 

     list.Add(1); 
     list.Add(2); 
     list.Add(3); 
     list.Add(4); 

     CollectionWrapper<int> collection = new CollectionWrapper<int>(list); 

     Console.WriteLine("Count:{0}", collection.Count); 
     foreach (var x in collection) 
     { 
      Console.WriteLine(x); 
     } 

     foreach (var x in (IEnumerable)collection) 
     { 
      Console.WriteLine(x); 
     } 
    } 
} 

Gracias por sus sugerencias.

Edit: Ahora no se puede utilizar mal por Downcasting a List (o lo que sea).

+0

IEnumerableEx - agh, qué nombre :) – mackenir

+0

Me alegra que te haya gustado;) –

+0

Eso doesn ' t trabajo: la persona que llama podría simplemente volver a la lista y modificar la colección. Quieres un envoltorio genuino - mira mi respuesta. –

0

Una matriz puede ser echado a un IList, lo que hace que el IList de sólo lectura == true :)

+0

Y eso estaría bien si él/ella tuviera una matriz :) – leppie

4

Suena como lo que realmente quieres ReadOnlyCollection<T> - exponga como IList<T>, pero envolviendo la lista original como este solo obtenga un contenedor de solo lectura con un conteo apropiado.

+0

Pregunta: ¿El compilador captará algo que intente agregar a esa colección? –

+1

@Daniel: No si lo expones como IList . Si lo expone como ReadOnlyCollection , el compilador lo recogerá; ROC implementa IList .Agregar explícitamente. –

+0

@DanielAuger: No, pero el código aumentará NotSupportedException en tiempo de ejecución. –

0

Como menciona Jon Skeet, es mucho mejor utilizar System.Collections.ObjectModel.ReadOnlyCollection en lugar de crear su propia clase contenedora.

A continuación, puede implementar su muestra de la siguiente manera:

class Program { 
    static void Main(string[] args) { 
     List<int> list = new List<int>(); 
     list.Add(1); 
     list.Add(2); 
     list.Add(3); 
     list.Add(4); 

     ReadOnlyCollection<int> collection = new ReadOnlyCollection<int>(list); 
     Console.WriteLine("Count:{0}", collection.Count); 
     foreach (var x in collection) { 
      Console.WriteLine(x); 
     } 
     foreach (var x in (IEnumerable)collection) { 
      Console.WriteLine(x); 
     } 
    } 
} 
9

A partir de .Net 4.5, hay dos nuevas interfaces para esto: IReadOnlyCollection<T> y IReadOnlyList<T>.

IReadOnlyCollection<T> es IEnumerable<T> con una propiedad Count añadió, IReadOnlyList<T> añade también la indexación.

+2

¡Finalmente! ¿Qué les llevó tanto tiempo? –

Cuestiones relacionadas